/* * #%L * gitools-core * %% * Copyright (C) 2013 Universitat Pompeu Fabra - Biomedical Genomics group * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ package org.gitools.datasources.idmapper; import org.gitools.api.analysis.IProgressMonitor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; public class MappingEngine { private static final Logger logger = LoggerFactory.getLogger(MappingEngine.class); private static class Edge { private final MappingNode src; private final MappingNode dst; private final Mapper proc; public Edge(MappingNode src, MappingNode dst, Mapper proc) { this.src = src; this.dst = dst; this.proc = proc; } public MappingNode getSrc() { return src; } public MappingNode getDst() { return dst; } public Mapper getMapper() { return proc; } @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof Edge)) { return false; } Edge other = (Edge) obj; return src.equals(other.getSrc()) && dst.equals(other.getDst()); } @Override public int hashCode() { int hash = 7; hash = 67 * hash + (this.src != null ? this.src.hashCode() : 0); hash = 67 * hash + (this.dst != null ? this.dst.hashCode() : 0); return hash; } @Override public String toString() { return src.toString() + " --> " + dst.toString(); } } private static class Path { private final LinkedList<Step> steps = new LinkedList<>(); private final Set<MappingNode> visited = new HashSet<>(); public Path() { } public Path(MappingNode node) { steps.add(new Step(node)); } private Path(Path path, Step step) { steps.addAll(path.getSteps()); steps.add(step); } public MappingNode getLastNode() { return steps.getLast().getNode(); } public void addStep(Step step) { steps.add(step); visited.add(step.getNode()); } public int getLength() { return steps.size() - 1; } public LinkedList<Step> getSteps() { return steps; } private boolean visited(MappingNode node) { return visited.contains(node); } @Override public String toString() { StringBuilder sb = new StringBuilder(); Iterator<Step> it = steps.iterator(); if (it.hasNext()) { Step step = it.next(); sb.append(step.getNode()); while (it.hasNext()) { step = it.next(); sb.append(" --[").append(step.getMapper()); sb.append("]--> ").append(step.getNode()); } } return sb.toString(); } } private static class Step { private final MappingNode node; private final Mapper mapper; public Step(MappingNode node) { this(node, null); } public Step(MappingNode node, Mapper mapper) { this.node = node; this.mapper = mapper; } public MappingNode getNode() { return node; } public Mapper getMapper() { return mapper; } @Override public String toString() { return node.getId() + " {" + mapper.getName() + "}"; } } private final MappingContext context; private final List<Edge> edges; public MappingEngine() { this.context = new MappingContext(); this.edges = new ArrayList<>(); } public void addMapper(String src, String dst, Mapper proc) { addMapper(new StringMappingNode(src), new StringMappingNode(dst), proc); } void addMapper(MappingNode src, MappingNode dst, Mapper proc) { edges.add(new Edge(src, dst, proc)); } public MappingData run(String[] ids, String src, String dst, IProgressMonitor monitor) throws MappingException { monitor.begin("Mapping from " + src + " to " + dst + " ...", 4); MappingData data = new MappingData(src, src); if (ids != null) { data.identity(new HashSet<>(Arrays.asList(ids))); } monitor.info("Searching mapping path ..."); Path path = findPath(src, dst, monitor); if (path == null) { throw new MappingException("Unable to find a mapping path from " + src + " to " + dst); } monitor.debug("Mapping path: " + path); monitor.worked(1); LinkedList<Step> steps = path.getSteps(); Iterator<Step> it = steps.iterator(); Set<Mapper> initializedMappers = new HashSet<>(); it.next(); while (it.hasNext()) { Step step = it.next(); Mapper mapper = step.getMapper(); if (!initializedMappers.contains(mapper)) { mapper.initialize(context, monitor); initializedMappers.add(mapper); } } monitor.worked(1); it = steps.iterator(); MappingNode lastNode = it.next().getNode(); while (it.hasNext()) { Step step = it.next(); IProgressMonitor m = monitor.subtask(); m.begin("Mapping from " + lastNode + " to " + step.getNode() + " ...", 1); data = step.getMapper().map(context, data, lastNode, step.getNode(), m.subtask()); lastNode = step.getNode(); data.setDstNode(lastNode); data.removeEmptyKeys(); } monitor.worked(1); for (Mapper m : initializedMappers) m.finalize(context, monitor); return data; } public MappingData run(String src, String dst, IProgressMonitor monitor) throws MappingException { return run(null, src, dst, monitor); } private Path findPath(String src, String dst, IProgressMonitor monitor) { StringMappingNode dstNode = new StringMappingNode(dst); Path bestPath = null; int bestLength = Integer.MAX_VALUE; LinkedList<Path> paths = new LinkedList<>(); paths.offer(new Path(new StringMappingNode(src))); while (paths.size() > 0) { Path path = paths.poll(); if (path.getLength() >= bestLength) { continue; } MappingNode snode = path.getLastNode(); boolean generatorRequired = path.getLength() == 0; List<Step> steps = getSteps(snode, generatorRequired); for (Step step : steps) { if (step.getNode().equals(dstNode)) { if (bestPath == null) { bestPath = new Path(path, step); bestLength = bestPath.getLength(); break; } } else if (!path.visited(step.getNode())) { paths.add(new Path(path, step)); } } } return bestPath; } private List<Step> getSteps(MappingNode snode, boolean generatorRequired) { List<Step> steps = new ArrayList<>(); for (Edge edge : edges) { MappingNode src = edge.getSrc(); MappingNode dst = edge.getDst(); Mapper mapper = edge.getMapper(); if (generatorRequired && !mapper.isGenerator()) { continue; } if (src.equals(snode)) { steps.add(new Step(dst, mapper)); } else if (mapper.isBidirectional() && dst.equals(snode)) { steps.add(new Step(src, mapper)); } } return steps; } }