/******************************************************************************* * Copyright 2014 University of Southern California * * 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. * * This code was developed by the Information Integration Group as part * of the Karma project at the Information Sciences Institute of the * University of Southern California. For more information, publications, * and related projects, please see: http://www.isi.edu/integration ******************************************************************************/ package edu.isi.karma.kr2rml.planning; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DFSTriplesMapGraphDAGifier implements TriplesMapGraphDAGifier { private static Logger logger = LoggerFactory.getLogger(DFSTriplesMapGraphDAGifier.class); @Override public List<String> dagify(TriplesMapGraph graph, RootStrategy rootStrategy) { HashSet<String> triplesMapsIds = new HashSet<>(); if(graph.getTriplesMapIds().isEmpty()) { return new LinkedList<>(); } if(graph.getTriplesMapIds().size() == 1) { List<String> results = new LinkedList<>(); results.addAll(graph.getTriplesMapIds()); return results; } TriplesMapGraph newGraph = graph.copyGraph(triplesMapsIds); String rootTriplesMapId = newGraph.findRoot(rootStrategy); List<String> spilledNodes = cleanGraph(triplesMapsIds, newGraph, rootTriplesMapId); if(triplesMapsIds.isEmpty()) { return spilledNodes; } dfs(newGraph, rootTriplesMapId); spilledNodes.addAll(cleanGraph(triplesMapsIds, newGraph, rootTriplesMapId)); if(!triplesMapsIds.isEmpty()) { logger.error("Unable to create DAG for the following nodes: "+ triplesMapsIds.toString()); } return spilledNodes; } private List<String> cleanGraph(Set<String> triplesMapsIds, TriplesMapGraph newGraph, String rootTriplesMapId) { boolean modifications = true; List<String> spilledTriplesMaps = new LinkedList<>(); while(!triplesMapsIds.isEmpty() && modifications) { logger.trace("starting a cleaning cycle"); modifications = false; Iterator<String> ids = triplesMapsIds.iterator(); while(ids.hasNext()) { String triplesMapId = ids.next(); List<TriplesMapLink> links = newGraph.getAllNeighboringTriplesMap(triplesMapId); TriplesMap tm = newGraph.getTriplesMap(triplesMapId); if(tm == null) { logger.debug(triplesMapId + " was already spilled"); spilledTriplesMaps.add(triplesMapId); ids.remove(); modifications = true; continue; } if(links.size() <= 1 || allLinksAreIncoming(triplesMapId, links)) { List<TriplesMapLink> tempLinks = new LinkedList<>(); tempLinks.addAll(links); // leave the root alone unless it's empty! if(triplesMapId.compareTo(rootTriplesMapId) == 0) { logger.debug("working on root"); if(!links.isEmpty()) continue; logger.debug("root is being spilled"); } for(TriplesMapLink link : tempLinks) { logger.debug("Removing " + link.getPredicateObjectMapLink()); newGraph.removeLink(link); if(link.getSourceMap().getId().compareTo(triplesMapId) == 0 && (triplesMapId.compareTo(rootTriplesMapId) != 0)) { link.setIsFlipped(true); logger.debug("Flipping " + link.getPredicateObjectMapLink()); } } if(links.isEmpty()) { logger.debug("Spilling " + tm.getSubject().getRdfsType() + " " +triplesMapId); modifications = true; List<String> removedTriplesMaps = newGraph.removeTriplesMap(triplesMapId); spilledTriplesMaps.addAll(removedTriplesMaps); ids.remove(); } modifications = true; } } } return spilledTriplesMaps; } private boolean allLinksAreIncoming(String triplesMapId, List<TriplesMapLink> links) { for(TriplesMapLink link : links) { if(link.getSourceMap().getId().compareTo(triplesMapId) == 0 && !link.isFlipped()) { return false; } } logger.debug("all links are in coming " + triplesMapId); return true; } private void dfs(TriplesMapGraph graph, String rootTriplesMapId, Set<String> visited, String triplesMapId) { final String localTriplesMapId = triplesMapId; visited.add(triplesMapId); List<TriplesMapLink> links = graph.getAllNeighboringTriplesMap(triplesMapId); List<String> nodesToVisit = new LinkedList<>(); List<TriplesMapLink> sortedLinks = new LinkedList<>(); sortedLinks.addAll(links); Collections.sort(sortedLinks, new Comparator<TriplesMapLink>(){ @Override public int compare(TriplesMapLink o1, TriplesMapLink o2) { boolean o1IsSource = o1.getSourceMap().getId().compareTo(localTriplesMapId) == 0; boolean o2IsSource = o2.getSourceMap().getId().compareTo(localTriplesMapId) == 0; if((o1IsSource && o2IsSource)|| (!o1IsSource && !o2IsSource)) { return 0; } else if(o1IsSource && !o2IsSource) { return -1; } else { return 1; } } }); for(TriplesMapLink link : sortedLinks) { String nextNode = null; if(link.getSourceMap().getId().compareTo(triplesMapId) == 0) { nextNode = link.getTargetMap().getId(); } else { nextNode = link.getSourceMap().getId(); if(!visited.contains(nextNode)) { link.setIsFlipped(true); logger.debug("Flipping " + link.getPredicateObjectMapLink()); } } nodesToVisit.add(nextNode); } for(String nodeToVisit : nodesToVisit) { if(!visited.contains(nodeToVisit)) { dfs(graph, rootTriplesMapId, visited, nodeToVisit); } } } private void dfs(TriplesMapGraph graph, String rootTriplesMapId) { Set<String> visited = new HashSet<>(); dfs(graph, rootTriplesMapId, visited, rootTriplesMapId); } }