From 5fb67e6379b2756e904a12f60bad4d58eba21c38 Mon Sep 17 00:00:00 2001 From: Micah Date: Wed, 10 Nov 2021 17:55:57 -0600 Subject: [PATCH 01/10] Initial refactor for readability --- pom.xml | 11 +- .../utils/impl/BoundedContextUtilsImpl.java | 445 ++++++++---------- .../utils/impl/util/EntityCollection.java | 93 ++++ .../utils/impl/util/SimilarityRecord.java | 21 + 4 files changed, 321 insertions(+), 249 deletions(-) create mode 100644 src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/EntityCollection.java create mode 100644 src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/SimilarityRecord.java diff --git a/pom.xml b/pom.xml index ed3a423..821cb9b 100644 --- a/pom.xml +++ b/pom.xml @@ -12,8 +12,8 @@ UTF-8 - 1.8 - 1.8 + 16 + 16 @@ -76,6 +76,13 @@ 2.8.6 + + + org.projectlombok + lombok + 1.18.22 + provided + diff --git a/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/BoundedContextUtilsImpl.java b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/BoundedContextUtilsImpl.java index e41e66f..3064362 100644 --- a/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/BoundedContextUtilsImpl.java +++ b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/BoundedContextUtilsImpl.java @@ -5,13 +5,14 @@ package edu.baylor.ecs.prophet.bounded.context.utils.impl; - import edu.baylor.ecs.cloudhubs.prophetdto.systemcontext.*; import edu.baylor.ecs.cloudhubs.prophetdto.systemcontext.Module; import edu.baylor.ecs.prophet.bounded.context.exception.FieldMappingException; import edu.baylor.ecs.prophet.bounded.context.utils.BoundedContextUtils; import edu.baylor.ecs.prophet.bounded.context.utils.SimilarityUtils; +import edu.baylor.ecs.prophet.bounded.context.utils.impl.util.EntityCollection; + import org.apache.commons.lang3.tuple.ImmutablePair; import java.util.*; @@ -19,254 +20,204 @@ /** * methods for creating a {@link BoundedContext} from a {@link SystemContext} + * * @author Ian laird */ public class BoundedContextUtilsImpl implements BoundedContextUtils { - // tools used for finding similarities - private SimilarityUtils similarityUtils = new SimilarityUtilsImpl(); - - public static double ENTITY_SIMILARITY_CUTOFF = 0.9; - - /** - * creates a bounded context for the system context - * @param systemContext the system - * @return the bounded context - */ - @Override - public BoundedContext createBoundedContext(SystemContext systemContext, boolean useWuPalmer) { - - // sanitize all of the name in the systemContext - NameStripper.sanitizeSystemContext(systemContext); - - Set modules = systemContext.getModules(); - Stack moduleStack = new Stack<>(); - for (Module m: modules) { - moduleStack.add(m.clone()); - } -// moduleStack.addAll(modules); - while(moduleStack.size() > 1) { - Module m1 = moduleStack.pop(); - Module m2 = moduleStack.pop(); - Module result = mergeModules(m1, m2, useWuPalmer); - if (result.getEntities().size() > 0) { - moduleStack.push(result); - } - } - - return new BoundedContext(systemContext.getSystemName(), moduleStack.size() > 0 ? moduleStack.get(0).getEntities() : null); - - } - - /** - * merges two modules into one module - * @param moduleOne one of the modules - * @param moduleTwo the other module - * @return a new module comprised of the other two - */ - @Override - public Module mergeModules(Module moduleOne, Module moduleTwo, boolean useWuPalmer){ - - // for each entity find the similarity it has to other entities - Map>>> - entitySimilarity = new HashMap<>(); - - // shows which entities in module two are encountered - Set mappedInTwo = new HashSet<>(); - - // get all entities in module one - moduleOne.getEntities() - - // for each entity in entity one add to entity similarity - .forEach(x -> entitySimilarity.put( - - // the current entity of module one - x, - - // create stream of entity two entities - moduleTwo.getEntities().stream() - - // create map - .collect(Collectors.toMap( - - // similarity of entity from module one and entity from module two - y -> similarityUtils.globalFieldSimilarity(x, y, useWuPalmer).getLeft(), - - // tuple of entity from module two - y -> new ImmutablePair<>(y, - //and the field mapping - similarityUtils.globalFieldSimilarity(x, y, useWuPalmer).getRight()), - (oldValue,newValue) -> newValue, - TreeMap::new - )) - )); - - Module newModule = new Module(moduleOne.getName().getName()); - - newModule.setEntities(new HashSet<>()); - - // sets the entities of the new module - newModule.getEntities().addAll( - - // stream of all entries in entity similarity - entitySimilarity.entrySet().stream() - - // if the similarity is strong enough - .filter(x -> { - // if it is not mapped to anything, no merging needs to be performed - if(x.getValue().isEmpty()){ - newModule.getEntities().add(x.getKey().clone()); - return false; - } - - Map.Entry>> val = x.getValue().lastEntry(); - double similarity = val.getKey(); - - // if the two entities should be merged - if (similarity > ENTITY_SIMILARITY_CUTOFF) { - // add the one mapped to - mappedInTwo.add(val.getValue().getLeft()); - - return true; - } else { - newModule.getEntities().add(x.getKey().copyWithNamePreface(moduleOne.getName() + "::")); - //val.getValue().getLeft().getEntityName().setFullName(moduleTwo.getName() + val.getValue().getLeft().getEntityName().getFullName() + "::"); - //newModule.getEntities().add(val.getValue().getLeft().copyWithNamePreface(moduleTwo.getName() + "::")); - - return false; - } - - }) - - // map each mapping between entities to a new merged entity - .map(x -> mergeEntities(x.getKey(), x.getValue().lastEntry().getValue().getLeft(), x.getValue().lastEntry().getValue().getRight())) - - // collect as a list - .collect(Collectors.toList()) - ); - - // now add all of the entities in module two that were not mapped to - for(Entity e : moduleTwo.getEntities()){ - if(!mappedInTwo.contains(e)){ - newModule.getEntities().add(e); - } - } - - return newModule; - } - - /** - * merges two entities together using the field mapping - * @param one the first entity to merge - * @param two the second entity to merge - * @param fieldMapping the mapping between the fields of the entities - * @return the newly created merged entity - */ - @Override - public Entity mergeEntities(Entity one, Entity two, Map fieldMapping) { - - // the entity that is to be returned - Entity newEntity = new Entity(one.getEntityName()); - - // get the fields of the second entity - Set entityTwoFields = new HashSet<>(two.getFields()); - Field toAdd = null; - - if(Objects.isNull(fieldMapping)){ - fieldMapping = new HashMap<>(); - } - - Set alreadyEncountered = new HashSet<>(); - - // make sure that all fields in the field mapping are also in f1 and that no two map to the same value - for(final Map.Entry f : fieldMapping.entrySet()){ - if(Objects.isNull(f.getValue())){ - continue; - } - // make sure that the key exists - if(!one.getFields().contains(f.getKey())){ - throw new FieldMappingException(); - } - //make sure that the value exists - if(!entityTwoFields.contains(f.getValue())){ - throw new FieldMappingException(); - } - // if the second has already been mapped too - if(!alreadyEncountered.add(f.getValue())){ - throw new FieldMappingException(); - } - } - - // for each field in entity one - for (Field f1 : one.getFields()){ - - // get the field that this field in entity one maps to - Field f2 = fieldMapping.get(f1); - toAdd = f1; - - String preface = one.getEntityName() + "::"; - - if (f2 != null) { - - //make sure that mapped to field is present in entity 2 - entityTwoFields.remove(f2); - - if (f1.isReference() && f2.isReference() && !f1.equals(f2)) { - Field twoCopy = f2.clone(); - String newName = two.getEntityName().getName() + "::" + twoCopy.getName().getName(); - twoCopy.getName().setFullName(newName); - newEntity.getFields().add(twoCopy); - toAdd = f1; - } - - // merge the fields into one field - else { - preface = ""; - toAdd = mergeFields(f1, f2); - } - } - - // add the field - // TODO what if a field of this name already exists? - Field newField = toAdd.clone(); - newField.getName().setFullName(preface + newField.getName().getName()); - newEntity.getFields().add(newField); - } - - // add all of the remaining fields in entity 2 - // make a copy of all of the field in entity 2 - Set entityTwoFieldsMapped = entityTwoFields.stream().map(x -> { - Field fieldCopy = x.clone(); - fieldCopy.getName().setFullName(two.getEntityName().getName() + "::" + fieldCopy.getName().getName()); - return fieldCopy; - }).collect(Collectors.toSet()); - newEntity.getFields().addAll(entityTwoFieldsMapped); - - return newEntity; - } - - /** - * merges two fields into one field - * @param one the first field to merge - * @param two the second field to merge - * @return the new field - */ - @Override - public Field mergeFields(Field one, Field two) { - String name = one.getName().getName(); - String type = Type.get(one.getType()).ordinal() < Type.get(two.getType()).ordinal() ? two.getType() : one.getType(); - - Field toReturn = new Field(type, name); - - // set isCollection - toReturn.setCollection(one.isCollection() || two.isCollection()); - - // set the annotations - toReturn.setAnnotations(one.getAnnotations()); - toReturn.getAnnotations().addAll(two.getAnnotations()); - - toReturn.setReference(one.isReference() | two.isReference()); - - return toReturn; - } + // tools used for finding similarities + private SimilarityUtils similarityUtils = new SimilarityUtilsImpl(); + + public static double ENTITY_SIMILARITY_CUTOFF = 0.9; + + /** + * creates a bounded context for the system context + * + * @param systemContext the system + * @return the bounded context + */ + @Override + public BoundedContext createBoundedContext(SystemContext systemContext, boolean useWuPalmer) { + + // sanitize all of the name in the systemContext + NameStripper.sanitizeSystemContext(systemContext); + + Set modules = systemContext.getModules(); + Stack moduleStack = new Stack<>(); + for (Module m : modules) { + moduleStack.add(m.clone()); + } + + while (moduleStack.size() > 1) { + Module m1 = moduleStack.pop(); + Module m2 = moduleStack.pop(); + Module result = mergeModules(m1, m2, useWuPalmer); + if (result.getEntities().size() > 0) { + moduleStack.push(result); + } + } + + return new BoundedContext(systemContext.getSystemName(), + moduleStack.size() > 0 ? moduleStack.get(0).getEntities() : null); + + } + + /** + * merges two modules into one module + * + * @param moduleOne one of the modules + * @param moduleTwo the other module + * @return a new module comprised of the other two + */ + @Override + public Module mergeModules(Module moduleOne, Module moduleTwo, boolean useWuPalmer) { + + // for each entity find the similarity it has to other entities + EntityCollection entitySimilarity = new EntityCollection(moduleOne, moduleTwo, ENTITY_SIMILARITY_CUTOFF, + (e1, e2) -> similarityUtils.globalFieldSimilarity(e1, e2, useWuPalmer)); + + // shows which entities in module two are encountered + Set mappedInTwo = new HashSet<>(); + + // Generate blank new module + Module newModule = new Module(moduleOne.getName().getName()); + newModule.setEntities(entitySimilarity.getDistinctEntities()); + + // Merge all duplicate entities and add to the new module + newModule.getEntities().addAll(entitySimilarity.getSimilarEntities().stream() + + // Map entity/duplicate mappings to a merged entity + .map(x -> mergeEntities(x.entity(), x.similarEntity(), x.fieldMap())) + + // collect as a list + .toList()); + + // now add all of the entities in module two that were not mapped to + for (Entity e : moduleTwo.getEntities()) { + if (!mappedInTwo.contains(e)) { + newModule.getEntities().add(e); + } + } + + return newModule; + } + + /** + * merges two entities together using the field mapping + * + * @param one the first entity to merge + * @param two the second entity to merge + * @param fieldMapping the mapping between the fields of the entities + * @return the newly created merged entity + */ + @Override + public Entity mergeEntities(Entity one, Entity two, Map fieldMapping) { + + // the entity that is to be returned + Entity newEntity = new Entity(one.getEntityName()); + + // get the fields of the second entity + Set entityTwoFields = new HashSet<>(two.getFields()); + Field toAdd = null; + + if (Objects.isNull(fieldMapping)) { + fieldMapping = new HashMap<>(); + } + + Set alreadyEncountered = new HashSet<>(); + + // make sure that all fields in the field mapping exist in their respective + // entities, and that no two map to the same value + for (final Map.Entry f : fieldMapping.entrySet()) { + if (Objects.isNull(f.getValue())) { + continue; + } + + // Make sure mapped fields exist in their respective entities + if (!one.getFields().contains(f.getKey())) { + throw new FieldMappingException(); + } + if (!entityTwoFields.contains(f.getValue())) { + throw new FieldMappingException(); + } + + // if the second has already been mapped too + if (!alreadyEncountered.add(f.getValue())) { + throw new FieldMappingException(); + } + } + + // for each field in entity one + for (Field f1 : one.getFields()) { + + // get the field that this field in entity one maps to + Field f2 = fieldMapping.get(f1); + toAdd = f1; + String preface = one.getEntityName() + "::"; + + if (f2 != null) { + + // make sure that mapped to field is present in entity 2 + entityTwoFields.remove(f2); + + if (f1.isReference() && f2.isReference() && !f1.equals(f2)) { + Field twoCopy = f2.clone(); + String newName = two.getEntityName().getName() + "::" + twoCopy.getName().getName(); + twoCopy.getName().setFullName(newName); + newEntity.getFields().add(twoCopy); + toAdd = f1; + } + + // merge the fields into one field + else { + preface = ""; + toAdd = mergeFields(f1, f2); + } + } + + // add the field + // TODO what if a field of this name already exists? + Field newField = toAdd.clone(); + newField.getName().setFullName(preface + newField.getName().getName()); + newEntity.getFields().add(newField); + } + + // add all of the remaining fields in entity 2 + // make a copy of all of the field in entity 2 + Set entityTwoFieldsMapped = entityTwoFields.stream().map(x -> { + Field fieldCopy = x.clone(); + fieldCopy.getName().setFullName(two.getEntityName().getName() + "::" + fieldCopy.getName().getName()); + return fieldCopy; + }).collect(Collectors.toSet()); + newEntity.getFields().addAll(entityTwoFieldsMapped); + + return newEntity; + } + + /** + * merges two fields into one field + * + * @param one the first field to merge + * @param two the second field to merge + * @return the new field + */ + @Override + public Field mergeFields(Field one, Field two) { + String name = one.getName().getName(); + String type = Type.get(one.getType()).ordinal() < Type.get(two.getType()).ordinal() ? two.getType() + : one.getType(); + + Field toReturn = new Field(type, name); + + // set isCollection + toReturn.setCollection(one.isCollection() || two.isCollection()); + + // set the annotations + toReturn.setAnnotations(one.getAnnotations()); + toReturn.getAnnotations().addAll(two.getAnnotations()); + + toReturn.setReference(one.isReference() || two.isReference()); + + return toReturn; + } } diff --git a/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/EntityCollection.java b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/EntityCollection.java new file mode 100644 index 0000000..1e00685 --- /dev/null +++ b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/EntityCollection.java @@ -0,0 +1,93 @@ +package edu.baylor.ecs.prophet.bounded.context.utils.impl.util; + +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.tuple.ImmutablePair; + +import edu.baylor.ecs.cloudhubs.prophetdto.systemcontext.Entity; +import edu.baylor.ecs.cloudhubs.prophetdto.systemcontext.Field; +import edu.baylor.ecs.cloudhubs.prophetdto.systemcontext.Module; +import lombok.Getter; + +/** + * An container that takes two modules and stores all entities within the first + * based on whether they have a likely duplicate in another specified module. + * + * @author Micah + */ +@Getter +public class EntityCollection { + + /** Mapping of entity */ + private final Set similarEntities = new HashSet<>(); + + /** Entities with no similar entities to map to */ + private final Set distinctEntities = new HashSet<>(); + + /** + * Construct a collection that partitions the merged + * + * @param mod1 + * @param mod2 + * @param matchThreshhold + * @param computeSimilarity + */ + public EntityCollection(Module mod1, Module mod2, double matchThreshhold, + BiFunction>> computeSimilarity) { + // Validate input + Objects.requireNonNull(mod1, "mod1 cannot be null"); + Objects.requireNonNull(mod2, "mod2 cannot be null"); + + // Determine which entities have no matches at all + var splitEntityRecords = mod1.getEntities().stream() + // Map entities in module 1 to their most similar counterpart in module 2 + .map(mod1Entity -> findMostSimilar(mod1Entity, mod2, computeSimilarity)) + // Split into those with a match in the module 2 and those without + .collect(Collectors.partitioningBy(Optional::isPresent)); + + // All entities with no match are distinct; put them in distinct lists + splitEntityRecords.get(false).stream().map(entry -> entry.get().getRight().entity().clone()) + .forEach(distinctEntities::add); + + // Use similarity score to categorize similar and distinct entities + splitEntityRecords.get(true).forEach(similarityRecord -> { + ImmutablePair record = similarityRecord.get(); + Entity entity = record.getRight().entity(); + if (record.getLeft() > matchThreshhold) { + // Distinct, store to distinct list + distinctEntities.add(entity.copyWithNamePreface(mod1.getName() + "::")); + } else { + // Similar, store to similar map + similarEntities.add(record.getRight()); + } + }); + } + + /** + * Find the highest similarity entity within the provided module + * + * @param entity Entity to compare + * @param mod2 Module to search + * @param computeSimilarity Callback to compute similarity between entities + * @return tuple (similarity, entity, field_mapping) + */ + private Optional> findMostSimilar(Entity entity, Module mod2, + BiFunction>> computeSimilarity) { + return mod2.getEntities().stream().map(mod2Entity -> { + // Convert into a (similarity, entity, field_mappings) tuple + var simil = computeSimilarity.apply(entity, mod2Entity); + return ImmutablePair.of(simil.getLeft(), new SimilarityRecord(entity, mod2Entity, simil.getRight())); + }) + // Sort by similarity score, highest to lowest + .sorted((entity1, entity2) -> entity2.getLeft().compareTo(entity1.getLeft())) + + // Take highest score + .findFirst(); + } +} diff --git a/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/SimilarityRecord.java b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/SimilarityRecord.java new file mode 100644 index 0000000..6e1b546 --- /dev/null +++ b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/SimilarityRecord.java @@ -0,0 +1,21 @@ +package edu.baylor.ecs.prophet.bounded.context.utils.impl.util; + +import java.util.Map; +import java.util.Objects; + +import edu.baylor.ecs.cloudhubs.prophetdto.systemcontext.Entity; +import edu.baylor.ecs.cloudhubs.prophetdto.systemcontext.Field; + +/** + * Wraps an entity, an entity that has been identified as "similar", and the + * mapping between which entity fields are similar. + * + * @author Micah + */ +public record SimilarityRecord(Entity entity, Entity similarEntity, Map fieldMap) { + public SimilarityRecord { + Objects.requireNonNull(entity, "entity cannot be null"); + Objects.requireNonNull(similarEntity, "similarEntity cannot be null"); + Objects.requireNonNull(fieldMap, "fieldMap cannot be null"); + } +} From b0929b9f8043f98d340b1c22e373e45e7af8b790 Mon Sep 17 00:00:00 2001 From: Micah Date: Thu, 11 Nov 2021 16:36:10 -0600 Subject: [PATCH 02/10] Make missed updates --- .../context/repository/DatabseRepository.java | 30 -------------- .../impl/DatabseRepositoryImpl.java | 40 ------------------- .../utils/impl/BoundedContextUtilsImpl.java | 21 ++-------- .../utils/impl/util/EntityCollection.java | 27 ++++++++----- 4 files changed, 19 insertions(+), 99 deletions(-) delete mode 100644 src/main/java/edu/baylor/ecs/prophet/bounded/context/repository/DatabseRepository.java delete mode 100644 src/main/java/edu/baylor/ecs/prophet/bounded/context/repository/impl/DatabseRepositoryImpl.java diff --git a/src/main/java/edu/baylor/ecs/prophet/bounded/context/repository/DatabseRepository.java b/src/main/java/edu/baylor/ecs/prophet/bounded/context/repository/DatabseRepository.java deleted file mode 100644 index b82b7fe..0000000 --- a/src/main/java/edu/baylor/ecs/prophet/bounded/context/repository/DatabseRepository.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright 2019, Cloud Innovation Labs, All rights reserved - * Version: 1.0 - */ - -package edu.baylor.ecs.prophet.bounded.context.repository; - -import edu.baylor.ecs.cloudhubs.prophetdto.systemcontext.BoundedContext; -import edu.baylor.ecs.cloudhubs.prophetdto.systemcontext.SystemContext; - -/** - * Interface to communicate with neo4j database - */ -public interface DatabseRepository { - - /** - * get all context models from the database based on system name - * @param systemName the name of the system - * @return the system context - */ - SystemContext getAllEntityClassesInSystem(String systemName); - - /** - * persist data to the DB - * @param boundedContext the bounded context to persist - * @return the bounded context that was persisted - */ - BoundedContext createBoundedContext(BoundedContext boundedContext); - -} diff --git a/src/main/java/edu/baylor/ecs/prophet/bounded/context/repository/impl/DatabseRepositoryImpl.java b/src/main/java/edu/baylor/ecs/prophet/bounded/context/repository/impl/DatabseRepositoryImpl.java deleted file mode 100644 index 9b197fe..0000000 --- a/src/main/java/edu/baylor/ecs/prophet/bounded/context/repository/impl/DatabseRepositoryImpl.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright 2019, Cloud Innovation Labs, All rights reserved - * Version: 1.0 - */ - -package edu.baylor.ecs.prophet.bounded.context.repository.impl; - - // import edu.baylor.ecs.cloudhubs.prophet.metamodel.service.BoundedContextDatabaseService; -import edu.baylor.ecs.cloudhubs.prophetdto.systemcontext.BoundedContext; -import edu.baylor.ecs.cloudhubs.prophetdto.systemcontext.SystemContext; -import edu.baylor.ecs.prophet.bounded.context.repository.DatabseRepository; - -/** - * @author Ian Laird - * @see DatabseRepository - */ -public class DatabseRepositoryImpl implements DatabseRepository { - - /** - * gets {@link SystemContext} from a system name - * @param systemName the name of the system - * @return the system context - */ - @Override - public SystemContext getAllEntityClassesInSystem(String systemName) { - return null; - //return BoundedContextDatabaseService.getAllEntityClassesInSystem(systemName); - } - - /** - * saves {@link BoundedContext} to the DB - * @param boundedContext the bounded context to persist - * @return the saved bounded context - */ - @Override - public BoundedContext createBoundedContext(BoundedContext boundedContext) { - return null; - //return BoundedContextDatabaseService.createBoundedContext(boundedContext); - } -} diff --git a/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/BoundedContextUtilsImpl.java b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/BoundedContextUtilsImpl.java index 3064362..12843f3 100644 --- a/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/BoundedContextUtilsImpl.java +++ b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/BoundedContextUtilsImpl.java @@ -13,8 +13,6 @@ import edu.baylor.ecs.prophet.bounded.context.utils.SimilarityUtils; import edu.baylor.ecs.prophet.bounded.context.utils.impl.util.EntityCollection; -import org.apache.commons.lang3.tuple.ImmutablePair; - import java.util.*; import java.util.stream.Collectors; @@ -28,7 +26,7 @@ public class BoundedContextUtilsImpl implements BoundedContextUtils { // tools used for finding similarities private SimilarityUtils similarityUtils = new SimilarityUtilsImpl(); - public static double ENTITY_SIMILARITY_CUTOFF = 0.9; + public static final double ENTITY_SIMILARITY_CUTOFF = 0.9; /** * creates a bounded context for the system context @@ -76,9 +74,6 @@ public Module mergeModules(Module moduleOne, Module moduleTwo, boolean useWuPalm EntityCollection entitySimilarity = new EntityCollection(moduleOne, moduleTwo, ENTITY_SIMILARITY_CUTOFF, (e1, e2) -> similarityUtils.globalFieldSimilarity(e1, e2, useWuPalmer)); - // shows which entities in module two are encountered - Set mappedInTwo = new HashSet<>(); - // Generate blank new module Module newModule = new Module(moduleOne.getName().getName()); newModule.setEntities(entitySimilarity.getDistinctEntities()); @@ -86,18 +81,8 @@ public Module mergeModules(Module moduleOne, Module moduleTwo, boolean useWuPalm // Merge all duplicate entities and add to the new module newModule.getEntities().addAll(entitySimilarity.getSimilarEntities().stream() - // Map entity/duplicate mappings to a merged entity - .map(x -> mergeEntities(x.entity(), x.similarEntity(), x.fieldMap())) - - // collect as a list - .toList()); - - // now add all of the entities in module two that were not mapped to - for (Entity e : moduleTwo.getEntities()) { - if (!mappedInTwo.contains(e)) { - newModule.getEntities().add(e); - } - } + // Map entity/duplicate mappings to a list of merged entities + .map(x -> mergeEntities(x.entity(), x.similarEntity(), x.fieldMap())).toList()); return newModule; } diff --git a/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/EntityCollection.java b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/EntityCollection.java index 1e00685..61a63d1 100644 --- a/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/EntityCollection.java +++ b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/EntityCollection.java @@ -44,29 +44,34 @@ public EntityCollection(Module mod1, Module mod2, double matchThreshhold, Objects.requireNonNull(mod1, "mod1 cannot be null"); Objects.requireNonNull(mod2, "mod2 cannot be null"); - // Determine which entities have no matches at all + // Split entities into two sets: those with a match, and those without var splitEntityRecords = mod1.getEntities().stream() - // Map entities in module 1 to their most similar counterpart in module 2 .map(mod1Entity -> findMostSimilar(mod1Entity, mod2, computeSimilarity)) - // Split into those with a match in the module 2 and those without .collect(Collectors.partitioningBy(Optional::isPresent)); - // All entities with no match are distinct; put them in distinct lists + // All entities with no match are distinct; put them in the distinct list splitEntityRecords.get(false).stream().map(entry -> entry.get().getRight().entity().clone()) .forEach(distinctEntities::add); - // Use similarity score to categorize similar and distinct entities - splitEntityRecords.get(true).forEach(similarityRecord -> { + // Use similarity score to find which matches are false positives + Set mappedInTwo = new HashSet<>(); // Index for which entities already handled + for (var similarityRecord : splitEntityRecords.get(true)) { + // Unwrap the optional/tuple ImmutablePair record = similarityRecord.get(); Entity entity = record.getRight().entity(); + + // If the similarity threshhold is exceeded, this is a true match; otherwise, + // record as a distinct entity. if (record.getLeft() > matchThreshhold) { - // Distinct, store to distinct list - distinctEntities.add(entity.copyWithNamePreface(mod1.getName() + "::")); - } else { - // Similar, store to similar map + mappedInTwo.add(entity); similarEntities.add(record.getRight()); + } else { + distinctEntities.add(entity.copyWithNamePreface(mod1.getName() + "::")); } - }); + } + + // Take all fields from module 2 not already handled, and record them + mod2.getEntities().stream().filter(e -> !mappedInTwo.contains(e)).forEach(distinctEntities::add); } /** From e05f1bb5aa6b81cd6a34325b8fdf286671f661d5 Mon Sep 17 00:00:00 2001 From: Micah Date: Fri, 12 Nov 2021 18:16:04 -0600 Subject: [PATCH 03/10] Update maven plugin compiler version --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 821cb9b..df2b95a 100644 --- a/pom.xml +++ b/pom.xml @@ -141,8 +141,8 @@ org.apache.maven.plugins maven-compiler-plugin - 8 - 8 + 16 + 16 From 347be7bf70f0a04d0941072f1347ed1a2c97ea9d Mon Sep 17 00:00:00 2001 From: Micah Date: Sat, 13 Nov 2021 13:40:06 -0600 Subject: [PATCH 04/10] Fix referenced Prophet DTO version --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index df2b95a..1aa9c18 100644 --- a/pom.xml +++ b/pom.xml @@ -63,12 +63,12 @@ prophet-dto 0.0.8 --> - + - com.github.cloudhubs - prophet-dto - expansion-base-SNAPSHOT - + com.github.cloudhubs + prophet-dto + modernize-prophet-dto-SNAPSHOT + com.google.code.gson From c2f5481c616f8b0e7b61a32485f6c014ff46a5ae Mon Sep 17 00:00:00 2001 From: Micah Date: Mon, 15 Nov 2021 18:24:11 -0600 Subject: [PATCH 05/10] Revert to Java 8 --- pom.xml | 252 +++++++++--------- .../utils/impl/util/SimilarityRecord.java | 13 +- 2 files changed, 129 insertions(+), 136 deletions(-) diff --git a/pom.xml b/pom.xml index 1aa9c18..f274ed3 100644 --- a/pom.xml +++ b/pom.xml @@ -1,151 +1,143 @@ - 4.0.0 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 - edu.baylor.ecs.cloudhubs - boundedcontext - 1.0-SNAPSHOT + edu.baylor.ecs.cloudhubs + boundedcontext + 1.0-SNAPSHOT - bounded-context + bounded-context - - UTF-8 - 16 - 16 - + + UTF-8 + 1.8 + 1.8 + - - - - org.junit.jupiter - junit-jupiter-api - 5.3.2 - test - - - - org.junit.jupiter - junit-jupiter-params - 5.3.2 - test - - + + + + org.junit.jupiter + junit-jupiter-api + 5.3.2 + test + + + + org.junit.jupiter + junit-jupiter-params + 5.3.2 + test + + + + + + org.apache.commons + commons-lang3 + 3.9 + - - - org.apache.commons - commons-lang3 - 3.9 - + + - - - - + - com.github.cloudhubs - ws4j - main-SNAPSHOT + com.github.cloudhubs + ws4j + main-SNAPSHOT - - - - - com.github.cloudhubs - prophet-dto - modernize-prophet-dto-SNAPSHOT - - - com.google.code.gson - gson - 2.8.6 - + + + + com.github.cloudhubs + prophet-dto + modernize-prophet-dto-SNAPSHOT + + + + com.google.code.gson + gson + 2.8.6 + - org.projectlombok - lombok - 1.18.22 - provided + org.projectlombok + lombok + 1.18.20 + provided - - - + + + - jitpack.io - https://jitpack.io + jitpack.io + https://jitpack.io - - - - - - maven-clean-plugin - 3.1.0 - - - - maven-resources-plugin - 3.0.2 - - - maven-compiler-plugin - 3.8.0 - - - maven-surefire-plugin - 2.22.1 - - - maven-jar-plugin - 3.0.2 - - - maven-install-plugin - 2.5.2 - - - maven-deploy-plugin - 2.8.2 - - - - maven-site-plugin - 3.7.1 - - - maven-project-info-reports-plugin - 3.0.0 - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 16 - 16 - - - - + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 16 + 16 + + + + \ No newline at end of file diff --git a/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/SimilarityRecord.java b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/SimilarityRecord.java index 6e1b546..e081d00 100644 --- a/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/SimilarityRecord.java +++ b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/SimilarityRecord.java @@ -5,6 +5,8 @@ import edu.baylor.ecs.cloudhubs.prophetdto.systemcontext.Entity; import edu.baylor.ecs.cloudhubs.prophetdto.systemcontext.Field; +import lombok.Data; +import lombok.NonNull; /** * Wraps an entity, an entity that has been identified as "similar", and the @@ -12,10 +14,9 @@ * * @author Micah */ -public record SimilarityRecord(Entity entity, Entity similarEntity, Map fieldMap) { - public SimilarityRecord { - Objects.requireNonNull(entity, "entity cannot be null"); - Objects.requireNonNull(similarEntity, "similarEntity cannot be null"); - Objects.requireNonNull(fieldMap, "fieldMap cannot be null"); - } +@Data +public class SimilarityRecord { + @NonNull Entity entity; + @NonNull Entity similarEntity; + @NonNull Map fieldMap; } From 78b985b13547e96181b3e3c9fc622498953e5852 Mon Sep 17 00:00:00 2001 From: Micah Date: Mon, 15 Nov 2021 18:27:26 -0600 Subject: [PATCH 06/10] Revert maven compile task version to Java 8 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f274ed3..8dfe483 100644 --- a/pom.xml +++ b/pom.xml @@ -133,8 +133,8 @@ org.apache.maven.plugins maven-compiler-plugin - 16 - 16 + 1.8 + 1.8 From 321d2547609c206e104644ab4ac73b9de6cc9df1 Mon Sep 17 00:00:00 2001 From: Micah Date: Mon, 15 Nov 2021 18:31:32 -0600 Subject: [PATCH 07/10] Fix interface naming (from reverting records) --- .../bounded/context/utils/impl/BoundedContextUtilsImpl.java | 2 +- .../bounded/context/utils/impl/util/EntityCollection.java | 4 ++-- .../bounded/context/utils/impl/util/SimilarityRecord.java | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/BoundedContextUtilsImpl.java b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/BoundedContextUtilsImpl.java index 12843f3..29ec49d 100644 --- a/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/BoundedContextUtilsImpl.java +++ b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/BoundedContextUtilsImpl.java @@ -82,7 +82,7 @@ public Module mergeModules(Module moduleOne, Module moduleTwo, boolean useWuPalm newModule.getEntities().addAll(entitySimilarity.getSimilarEntities().stream() // Map entity/duplicate mappings to a list of merged entities - .map(x -> mergeEntities(x.entity(), x.similarEntity(), x.fieldMap())).toList()); + .map(x -> mergeEntities(x.getEntity(), x.getSimilarEntity(), x.getFieldMap())).toList()); return newModule; } diff --git a/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/EntityCollection.java b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/EntityCollection.java index 61a63d1..a67e693 100644 --- a/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/EntityCollection.java +++ b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/EntityCollection.java @@ -50,7 +50,7 @@ public EntityCollection(Module mod1, Module mod2, double matchThreshhold, .collect(Collectors.partitioningBy(Optional::isPresent)); // All entities with no match are distinct; put them in the distinct list - splitEntityRecords.get(false).stream().map(entry -> entry.get().getRight().entity().clone()) + splitEntityRecords.get(false).stream().map(entry -> entry.get().getRight().getEntity().clone()) .forEach(distinctEntities::add); // Use similarity score to find which matches are false positives @@ -58,7 +58,7 @@ public EntityCollection(Module mod1, Module mod2, double matchThreshhold, for (var similarityRecord : splitEntityRecords.get(true)) { // Unwrap the optional/tuple ImmutablePair record = similarityRecord.get(); - Entity entity = record.getRight().entity(); + Entity entity = record.getRight().getEntity(); // If the similarity threshhold is exceeded, this is a true match; otherwise, // record as a distinct entity. diff --git a/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/SimilarityRecord.java b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/SimilarityRecord.java index e081d00..41d08cd 100644 --- a/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/SimilarityRecord.java +++ b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/SimilarityRecord.java @@ -6,6 +6,7 @@ import edu.baylor.ecs.cloudhubs.prophetdto.systemcontext.Entity; import edu.baylor.ecs.cloudhubs.prophetdto.systemcontext.Field; import lombok.Data; +import lombok.Getter; import lombok.NonNull; /** From 64ec71fdcfac1e90f6b4d83949a0acd700a516a6 Mon Sep 17 00:00:00 2001 From: Micah Date: Mon, 15 Nov 2021 18:33:11 -0600 Subject: [PATCH 08/10] Revert use of Java 10 `var` --- .../bounded/context/utils/impl/util/EntityCollection.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/EntityCollection.java b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/EntityCollection.java index a67e693..eb51490 100644 --- a/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/EntityCollection.java +++ b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/util/EntityCollection.java @@ -14,6 +14,7 @@ import edu.baylor.ecs.cloudhubs.prophetdto.systemcontext.Field; import edu.baylor.ecs.cloudhubs.prophetdto.systemcontext.Module; import lombok.Getter; +import lombok.var; /** * An container that takes two modules and stores all entities within the first From b01f47945dfa703da24a034b3ab828f4616a5565 Mon Sep 17 00:00:00 2001 From: Micah Date: Mon, 15 Nov 2021 18:48:15 -0600 Subject: [PATCH 09/10] Revert prophet-dto version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8dfe483..1473b4b 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ com.github.cloudhubs prophet-dto - modernize-prophet-dto-SNAPSHOT + expansion-base-SNAPSHOT From c90e0d25e4b879ece23953aaeea9c1640493980a Mon Sep 17 00:00:00 2001 From: Micah Date: Mon, 15 Nov 2021 18:56:46 -0600 Subject: [PATCH 10/10] Remove more future Java version features --- .../bounded/context/utils/impl/BoundedContextUtilsImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/BoundedContextUtilsImpl.java b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/BoundedContextUtilsImpl.java index 29ec49d..fee9e16 100644 --- a/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/BoundedContextUtilsImpl.java +++ b/src/main/java/edu/baylor/ecs/prophet/bounded/context/utils/impl/BoundedContextUtilsImpl.java @@ -82,7 +82,8 @@ public Module mergeModules(Module moduleOne, Module moduleTwo, boolean useWuPalm newModule.getEntities().addAll(entitySimilarity.getSimilarEntities().stream() // Map entity/duplicate mappings to a list of merged entities - .map(x -> mergeEntities(x.getEntity(), x.getSimilarEntity(), x.getFieldMap())).toList()); + .map(x -> mergeEntities(x.getEntity(), x.getSimilarEntity(), x.getFieldMap())) + .collect(Collectors.toList())); return newModule; }