use of eu.esdihumboldt.hale.common.align.model.EntityDefinition in project hale by halestudio.
the class InstanceVisitor method visit.
/**
* @see AbstractSourceToTargetVisitor#visit(SourceNode)
*/
@Override
public boolean visit(SourceNode source) {
if (source.getDefinition() instanceof TypeDefinition) {
if (instance == null)
return false;
// source root
if (source.getDefinition().equals(instance.getDefinition())) {
// check type filter (if any)
Filter filter = source.getEntityDefinition().getFilter();
if (filter != null && !filter.match(instance)) {
// instance does not match filter, don't descend further
return false;
/*
* XXX What about merged instances? Will this be OK for
* those? A type filter should only apply to the original
* instance if it is merged - but most filters should
* evaluate the same
*/
} else {
// also sets the node to defined
source.setValue(instance);
for (FamilyInstance child : instance.getChildren()) {
// Find fitting SourceNodes.
Collection<SourceNode> candidateNodes = tree.getRootSourceNodes(child.getDefinition());
if (candidateNodes.isEmpty()) {
/*
* No node found - but this may be because no
* property of the type is mapped, but there still
* might be child instances (in a Join) that have
* types with associated relations. To prevent those
* being skipped we add an artificial node
* representing the instance.
*/
candidateNodes = new ArrayList<>();
EntityDefinition entityDef = new TypeEntityDefinition(child.getDefinition(), SchemaSpaceID.SOURCE, null);
candidateNodes.add(new SourceNodeImpl(entityDef, null, false));
}
for (SourceNode candidateNode : candidateNodes) {
filter = candidateNode.getEntityDefinition().getFilter();
if (filter == null || filter.match(child)) {
// XXX add to all candidates!?
if (candidateNode.getValue() == null) {
candidateNode.setAnnotatedParent(source);
source.addAnnotatedChild(candidateNode);
} else {
// Duplicate here, because there is no
// guarantee, that the Duplication
// Visitor will visit candidateNode after
// this node.
SourceNodeImpl duplicateNode = new SourceNodeImpl(candidateNode.getEntityDefinition(), candidateNode.getParent(), false);
duplicateNode.setAnnotatedParent(source);
source.addAnnotatedChild(duplicateNode);
TransformationContext context = candidateNode.getContext();
duplicateNode.setContext(context);
if (context != null) {
context.duplicateContext(candidateNode, duplicateNode, Collections.<Cell>emptySet(), log);
} else {
/*
* Not sure what this really means if we
* get here.
*
* Best guess: Probably that we weren't
* able to determine how the duplication
* of this source can be propagted to
* the target. Thus the duplicated node
* will probably not have any
* connection.
*/
log.warn(log.createMessage("No transformation context for duplicated node of source " + candidateNode.getDefinition().getDisplayName(), null));
}
candidateNode = duplicateNode;
}
// run instance visitor on that annotated child
InstanceVisitor visitor = new InstanceVisitor(child, tree, log);
candidateNode.accept(visitor);
}
}
}
return true;
}
} else
return false;
} else {
Object parentValue = source.getParent().getValue();
if (parentValue == null || !(parentValue instanceof Group)) {
source.setDefined(false);
return false;
} else {
Group parentGroup = (Group) parentValue;
Definition<?> currentDef = source.getDefinition();
Object[] values = parentGroup.getProperty(currentDef.getName());
if (values == null) {
source.setDefined(false);
return false;
}
// check for contexts
EntityDefinition entityDef = source.getEntityDefinition();
// index context
Integer index = AlignmentUtil.getContextIndex(entityDef);
if (index != null) {
// only use the value at the given index, if present
if (index < values.length) {
// annotate with the value at the index
Object value = values[index];
source.setValue(value);
return true;
} else {
source.setDefined(false);
return false;
}
}
// condition context
Condition condition = AlignmentUtil.getContextCondition(entityDef);
if (condition != null) {
if (condition.getFilter() == null) {
// assume exclusion
source.setDefined(false);
return false;
}
// apply condition as filter on values and continue with
// those values
Collection<Object> matchedValues = new ArrayList<Object>();
for (Object value : values) {
// determine parent
Object parent = null;
SourceNode parentNode = source.getParent();
if (parentNode != null && parentNode.isDefined()) {
parent = parentNode.getValue();
}
// test the condition
if (AlignmentUtil.matchCondition(condition, value, parent)) {
matchedValues.add(value);
}
}
values = matchedValues.toArray();
}
// default behavior (default context)
if (values.length >= 1) {
// annotate with the first value
Object value = values[0];
source.setValue(value);
source.setAllValues(values);
} else {
source.setDefined(false);
return false;
}
if (values.length > 1) {
// handle additional values
Object[] leftovers = new Object[values.length - 1];
System.arraycopy(values, 1, leftovers, 0, leftovers.length);
source.setLeftovers(new LeftoversImpl(source, leftovers));
}
return true;
}
}
}
use of eu.esdihumboldt.hale.common.align.model.EntityDefinition in project hale by halestudio.
the class EntityAccessorUtil method createEntity.
/**
* Create an entity definition from a path.
*
* @param path the path, the topmost element has to represent an
* {@link EntityDefinition}, all other elements must represent
* {@link ChildContext}s
* @return the created entity definition or <code>null</code> if the path
* was <code>null</code>
*/
public static EntityDefinition createEntity(Path<PathElement> path) {
if (path == null) {
return null;
}
List<PathElement> elements = new ArrayList<>(path.getElements());
// create entity definition
PathElement top = elements.remove(0);
if (top.getRoot() == null) {
throw new IllegalArgumentException("Topmost path element must be an entity definition");
}
EntityDefinition entity = top.getRoot();
// collect type information
TypeDefinition type = entity.getType();
SchemaSpaceID schemaSpace = entity.getSchemaSpace();
Filter filter = entity.getFilter();
List<ChildContext> contextPath = new ArrayList<>(entity.getPropertyPath());
for (PathElement element : elements) {
ChildContext cc = element.getChild();
if (cc == null) {
throw new IllegalArgumentException("All child elements must be defined by a child context");
}
contextPath.add(cc);
}
return AlignmentUtil.createEntity(type, contextPath, schemaSpace, filter);
}
use of eu.esdihumboldt.hale.common.align.model.EntityDefinition in project hale by halestudio.
the class AbstractMergeCellMigratorTest method assertCellTargetEquals.
// test helpers
/**
* Check if the given cell's target matches the expected target.
*
* @param cell the cell to check
* @param targetDef the expected target entity (simple definition as name
* list)
*/
protected void assertCellTargetEquals(Cell cell, List<String> targetDef) {
Entity entity = CellUtil.getFirstEntity(cell.getTarget());
assertNotNull("Target entity", entity);
EntityDefinition def = entity.getDefinition();
assertDefEquals(targetDef, def);
}
use of eu.esdihumboldt.hale.common.align.model.EntityDefinition in project hale by halestudio.
the class JoinMergeMigrator method addSources.
private void addSources(MutableCell cell, EntityDefinition source, Cell match, SimpleLog log, boolean transferContext) {
if (match.getSource() != null) {
ListMultimap<String, Entity> sources = ArrayListMultimap.create(cell.getSource());
for (Entry<String, ? extends Entity> entry : match.getSource().entries()) {
Entity entity = entry.getValue();
if (transferContext) {
// transfer filter and contexts if possible
EntityDefinition withContexts = AbstractMigration.translateContexts(source, entity.getDefinition(), log);
entity = AlignmentUtil.createEntity(withContexts);
}
if (!sources.containsEntry(entry.getKey(), entity)) {
// add if not already present
sources.put(entry.getKey(), entity);
}
}
cell.setSource(sources);
} else {
log.error("Match for source {0} is invalid and does not have a source", source.getDefinition());
return;
}
}
use of eu.esdihumboldt.hale.common.align.model.EntityDefinition in project hale by halestudio.
the class AbstractMergeCellMigrator method mergeSources.
/**
* Update the cell sources.
*
* @param sources the old sources
* @param mergeIndex the merge index
* @param originalCell the original cell
* @param migration the alignment migration (may be useful for cases where
* only entity replacement needs to be done)
* @param getCellMigrator functions that yields a cell migrator for a
* function (may be useful for cases where only entity
* replacement needs to be done)
* @param log the migration process log
* @return the merged cell or cells
*/
protected Iterable<MutableCell> mergeSources(ListMultimap<String, ? extends Entity> sources, MergeIndex mergeIndex, Cell originalCell, AlignmentMigration migration, Function<String, CellMigrator> getCellMigrator, SimpleLog log) {
// XXX relevant here at all?
boolean transferBase = true;
if (sources.size() == 1) {
EntityDefinition source = sources.values().iterator().next().getDefinition();
List<Cell> matches = mergeIndex.getCellsForTarget(source);
List<String> storedMessages = new ArrayList<>();
boolean inaccurateMatch = false;
if (matches.isEmpty()) {
// try to find match via parent (in specific cases)
matches = findParentMatch(source, mergeIndex);
if (!matches.isEmpty()) {
inaccurateMatch = true;
// message may not be added in every case, because it may be
// a duplicate
// storedMessages.add(MessageFormat
// .format("Inaccurate match of {0} via parent entity", source));
}
}
if (!matches.isEmpty()) {
List<MutableCell> cells = new ArrayList<>();
for (Cell match : matches) {
// the original cell
if (isDirectMatch(match)) {
MigrationOptions replaceSource = new MigrationOptionsImpl(true, false, transferBase);
cells.add(getCellMigrator.apply(originalCell.getTransformationIdentifier()).updateCell(originalCell, migration, replaceSource, log));
} else // matching cell
if (isDirectMatch(originalCell)) {
MigrationOptions replaceTarget = new MigrationOptionsImpl(false, true, transferBase);
AlignmentMigration cellMigration = new AbstractMigration() {
@Override
protected Optional<EntityDefinition> findMatch(EntityDefinition entity) {
Entity target = CellUtil.getFirstEntity(originalCell.getTarget());
if (target != null) {
return Optional.ofNullable(target.getDefinition());
}
return Optional.empty();
}
};
MutableCell newCell = getCellMigrator.apply(match.getTransformationIdentifier()).updateCell(match, cellMigration, replaceTarget, log);
SimpleLog cellLog = SimpleLog.all(log, new CellLog(newCell, CELL_LOG_CATEGORY));
// source of original cell may have
// filters/conditions/contexts that are not applied by
// changing the target
// try to apply source contexts
Entity originalSource = CellUtil.getFirstEntity(originalCell.getSource());
applySourceContexts(newCell, originalSource, cellLog);
cells.add(newCell);
} else {
// otherwise, use custom logic to try to combine cells
MutableCell newCell = new DefaultCell(originalCell);
SimpleLog cellLog = SimpleLog.all(log, new CellLog(newCell, CELL_LOG_CATEGORY));
C context = newContext(originalCell);
// reset source
newCell.setSource(ArrayListMultimap.create());
if (inaccurateMatch) {
cellLog.warn(MessageFormat.format("Inaccurate match of {0} via parent entity", source));
}
mergeSource(newCell, sources.keys().iterator().next(), source, match, originalCell, cellLog, context, migration, mergeIndex);
finalize(newCell, migration, context, cellLog);
cells.add(newCell);
}
}
if (!cells.isEmpty() && !storedMessages.isEmpty()) {
// add stored messages
cells.forEach(cell -> {
CellLog cLog = new CellLog(cell, CELL_LOG_CATEGORY);
storedMessages.forEach(msg -> cLog.warn(msg));
});
}
return cells;
} else {
// no match -> remove?
// rather add original + documentation
MutableCell newCell = new DefaultCell(originalCell);
SimpleLog cellLog = SimpleLog.all(log, new CellLog(newCell, CELL_LOG_CATEGORY));
cellLog.warn("No match for source {0} found, unable to associate to new source schema", source);
return Collections.singleton(newCell);
}
} else {
// handle each source
// collects messages in case all matches are direct matches
List<String> directMessages = new ArrayList<>();
// determine if all matches are direct
boolean allDirect = sources.entries().stream().allMatch(source -> {
List<Cell> matches = mergeIndex.getCellsForTarget(source.getValue().getDefinition());
if (matches.isEmpty()) {
directMessages.add(MessageFormat.format("No match was found for source {0}, please check how this can be compensated.", source.getValue().getDefinition()));
// if there is no match, treat it as direct match
return true;
} else {
if (matches.size() > 1) {
directMessages.add(MessageFormat.format("Multiple matches for source {0}, only one was taken into account", source.getValue().getDefinition()));
}
return isDirectMatch(matches.get(0));
}
});
MutableCell newCell;
if (allDirect) {
// if the matching are all Retype/Rename, replace sources of
// the original cell
MigrationOptions replaceSource = new MigrationOptionsImpl(true, false, transferBase);
newCell = getCellMigrator.apply(originalCell.getTransformationIdentifier()).updateCell(originalCell, migration, replaceSource, log);
// add messages from match check
SimpleLog cellLog = SimpleLog.all(log, new CellLog(newCell, CELL_LOG_CATEGORY));
directMessages.forEach(msg -> cellLog.warn(msg));
} else {
// handle each source separately
newCell = new DefaultCell(originalCell);
SimpleLog cellLog = SimpleLog.all(log, new CellLog(newCell, CELL_LOG_CATEGORY));
C context = newContext(originalCell);
// reset source
newCell.setSource(ArrayListMultimap.create());
for (Entry<String, ? extends Entity> source : sources.entries()) {
List<Cell> matches = mergeIndex.getCellsForTarget(source.getValue().getDefinition());
if (!matches.isEmpty()) {
Cell match = matches.get(0);
mergeSource(newCell, source.getKey(), source.getValue().getDefinition(), match, originalCell, cellLog, context, migration, mergeIndex);
if (matches.size() > 1) {
// FIXME how can we deal w/ multiple matches?
cellLog.warn("Multiple matches for source {0}, only one was handled", source.getValue().getDefinition());
}
} else {
// no match, just not add source?
cellLog.warn("No match was found for source {0}, please check how this can be compensated.", source.getValue().getDefinition());
}
}
finalize(newCell, migration, context, cellLog);
}
return Collections.singleton(newCell);
}
}
Aggregations