io.konig.core.showl.MappingStrategy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of konig-core Show documentation
Show all versions of konig-core Show documentation
A library for core classes (Graph, Vertex, Edge, etc.)
package io.konig.core.showl;
/*
* #%L
* Konig Core
* %%
* Copyright (C) 2015 - 2019 Gregory McFall
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.konig.core.vocab.Konig;
public class MappingStrategy {
public static final Logger logger = LoggerFactory.getLogger(MappingStrategy.class);
private ShowlMappingFilter filter;
public MappingStrategy() {
}
public MappingStrategy(ShowlMappingFilter filter) {
this.filter = filter;
}
/**
* Select the source-to-target-mappings for the specified target NodeShape.
* As a side-effect, the selected mappings are stored in each ShowlDirectPropertyShape contained in the node,
* and the selected join conditions are stored in the target node.
*
* @param target The NodeShape whose mappings are to be selected.
* @return The set of properties for which no mapping was found.
*/
public List selectMappings(ShowlNodeShape target) {
if (logger.isTraceEnabled()) {
logger.trace("selecteMappings: target={}", target.getPath());
}
Set set = new LinkedHashSet<>();
List pool = new ArrayList<>();
buildPool(target, pool, set);
Map rankingMap = new LinkedHashMap<>();
for (ShowlJoinCondition join : set) {
rankingMap.put(join, new RankedJoinCondition(join));
if (logger.isTraceEnabled()) {
logger.trace("selectMappings: rankingMap put {}", join);
}
}
while (!pool.isEmpty()) {
int originalSize = pool.size();
updateRankings(rankingMap, pool);
RankedJoinCondition best=null;
do {
best = findBestJoinCondition(rankingMap.values());
if (best != null) {
if (addSelectedJoin(target, best)) {
rankingMap.remove(best.getJoin());
selectMappings(target, best.getJoin(), pool);
break;
} else {
best.invalidate();
logger.trace("selectMappings: Failed to generate join condition.");
}
}
} while (best != null);
if (pool.size() == originalSize) {
break;
}
}
return pool;
}
private void buildPool(ShowlNodeShape target, List pool, Set set) {
for (ShowlDirectPropertyShape direct : target.getProperties()) {
pool.add(direct);
if (logger.isTraceEnabled()) {
logger.trace("buildPool: added {}", direct.getPath());
}
for (ShowlMapping m : direct.getMappings()) {
if (filter != null) {
ShowlPropertyShape other = m.findOther(direct);
ShowlNodeShape sourceNode = other.getDeclaringShape();
if (!filter.allowMapping(sourceNode, target)) {
logger.trace("selectMappings: filtering {}", sourceNode);
continue;
}
}
set.add(m.getJoinCondition());
}
ShowlNodeShape valueShape = direct.getValueShape();
if (valueShape != null) {
buildPool(valueShape, pool, set);
} else {
// Does this logic really belong inside the else block?
// Is there a better way to handle this case?
ShowlPropertyShape peer = direct.getPeer();
if (peer != null) {
valueShape = peer.getValueShape();
if (valueShape != null) {
buildPeerPool(valueShape, pool, set);
}
}
}
}
}
private void buildPeerPool(ShowlNodeShape target, List pool, Set set) {
for (ShowlDerivedPropertyList list : target.getDerivedProperties()) {
for (ShowlPropertyShape ps : list) {
pool.add(ps);
if (logger.isTraceEnabled()) {
logger.trace("buildPool: added {}", ps.getPath());
}
for (ShowlMapping m : ps.getMappings()) {
if (filter != null) {
ShowlPropertyShape other = m.findOther(ps);
ShowlNodeShape sourceNode = other.getDeclaringShape();
if (!filter.allowMapping(sourceNode, target)) {
logger.trace("selectMappings: filtering {}", sourceNode);
continue;
}
}
set.add(m.getJoinCondition());
}
ShowlNodeShape valueShape = ps.getValueShape();
if (valueShape != null) {
buildPool(valueShape, pool, set);
} else {
// Does this logic really belong inside the else block?
// Is there a better way to handle this case?
ShowlPropertyShape peer = ps.getPeer();
if (peer != null) {
valueShape = peer.getValueShape();
if (valueShape != null) {
buildPeerPool(valueShape, pool, set);
}
}
}
}
}
}
private boolean addSelectedJoin(ShowlNodeShape targetNode, RankedJoinCondition ranked) {
ShowlJoinCondition rankedJoin = ranked.getJoin();
if (rankedJoin instanceof ShowlFromCondition) {
targetNode.addSelectedJoin(rankedJoin);
return true;
}
ShowlPropertyShape targetProperty = rankedJoin.propertyOf(targetNode);
ShowlPropertyShape sourceProperty = rankedJoin.otherProperty(targetProperty);
ShowlNodeShape sourceNode = sourceProperty.getDeclaringShape();
if (targetNode.getSelectedJoins().isEmpty()) {
targetNode.addSelectedJoin(new ShowlFromCondition(rankedJoin, sourceNode));
return true;
} else {
// Build a source-to-source join condition
List list = targetNode.getSelectedJoins();
for (int i=list.size()-1; i>=0; i--) {
ShowlJoinCondition prior = list.get(i);
ShowlSourceToSourceJoinCondition s2s = sourceToSourceJoin(prior, sourceNode);
if (s2s != null) {
targetNode.addSelectedJoin(s2s);
return true;
}
}
}
return false;
}
private ShowlSourceToSourceJoinCondition sourceToSourceJoin(ShowlJoinCondition prior, ShowlNodeShape b) {
// For now, we only support join by Id
ShowlNodeShape a = prior.focusNode();
ShowlPropertyShape aId = a.findProperty(Konig.id);
ShowlPropertyShape bId = b.findProperty(Konig.id);
if (aId != null && bId!=null) {
return new ShowlSourceToSourceJoinCondition(null, aId, bId);
}
return null;
}
private void selectMappings(ShowlNodeShape node, ShowlJoinCondition join, List pool) {
ShowlPropertyShape joinProperty = join.propertyOf(node);
Iterator sequence = pool.iterator();
while (sequence.hasNext()) {
ShowlPropertyShape p = sequence.next();
String action = "was NOT selected";
ShowlMapping m = p.getMapping(join);
if (m == null && p==joinProperty) {
m = new ShowlJoinMapping(join);
}
if (m != null) {
p.setSelectedMapping(m);
sequence.remove();
action = "was selected";
}
if (logger.isTraceEnabled()) {
logger.trace("selectMappings: {} {}", p.getPath(), action);
}
}
}
private RankedJoinCondition findBestJoinCondition(Collection values) {
int best = 0;
RankedJoinCondition result = null;
Iterator sequence = values.iterator();
while (sequence.hasNext()) {
RankedJoinCondition r = sequence.next();
if (r.getRanking()==0) {
sequence.remove();
} else if (r.getRanking() > best) {
best = r.getRanking();
result = r;
}
}
return result;
}
private void updateRankings(Map rankingMap,
List pool) {
for (RankedJoinCondition e : rankingMap.values()) {
e.reset();
}
for (ShowlPropertyShape p : pool) {
if (logger.isTraceEnabled()) {
logger.trace("updateRankings: updating {}", p.getPath());
}
for (ShowlMapping m : p.getMappings()) {
RankedJoinCondition r = rankingMap.get(m.getJoinCondition());
if (r != null) {
r.incrementRanking();
if (logger.isTraceEnabled()) {
logger.trace("updateRankings: ranking({}...{})={}",
m.getLeftProperty().getPath(),
m.getRightProperty().getPath(),
r.getRanking());
}
} else {
if (logger.isTraceEnabled()) {
logger.trace("updateRankings: ranking({}...{})=null",
m.getLeftProperty().getPath(),
m.getRightProperty().getPath());
}
}
}
}
}
private void updateNestedRanking(ShowlNodeShape valueShape, RankedJoinCondition r) {
if (logger.isTraceEnabled()) {
logger.trace("updateNestedRanking({})", valueShape.getPath());
}
}
private static class RankedJoinCondition {
private int ranking;
private ShowlJoinCondition join;
public RankedJoinCondition(ShowlJoinCondition join) {
this.join = join;
}
public void incrementRanking() {
ranking++;
}
public void reset() {
ranking = 0;
}
public int getRanking() {
return ranking;
}
public ShowlJoinCondition getJoin() {
return join;
}
public void invalidate() {
ranking = -1;
}
public String toString() {
return "RankedJoinCondition(ranking: " + ranking + ", join: " + join.toString()+")";
}
}
}