All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.interedition.collatex.dekker.matrix.MatchTableSelection Maven / Gradle / Ivy

Go to download

A Java library for collating textual sources, for example, to produce an apparatus.

There is a newer version: 1.7.1
Show newest version
/*
 * Copyright (c) 2015 The Interedition Development Group.
 *
 * This file is part of CollateX.
 *
 * CollateX 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.
 *
 * CollateX 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 CollateX.  If not, see .
 */

package eu.interedition.collatex.dekker.matrix;

import eu.interedition.collatex.VariantGraph;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

// @author: Ronald Haentjens Dekker
// Unselected islands reside in the islandMultimap.
// Selected islands reside in the fixedIsland Archipelago.
// Group the islands together by size;
// islands may change after commit islands
public class MatchTableSelection {
    Logger LOG = Logger.getLogger(MatchTableSelection.class.getName());
    private final Map> islandMultimap;
    private final Archipelago fixedIslands;
    //this fields are needed for the locking of table cells
    private final Set fixedRows;
    private final Set fixedVertices;
    private final MatchTable table;

    public MatchTableSelection(MatchTable table) {
        fixedRows = new HashSet<>();
        fixedVertices = new HashSet<>();
        this.table = table;
        this.fixedIslands = new Archipelago();
        islandMultimap = new HashMap<>();
        for (Island isl : table.getIslands()) {
            islandMultimap.computeIfAbsent(isl.size(), s -> new ArrayList<>()).add(isl);
        }
    }

    // copy constructor
    public MatchTableSelection(MatchTableSelection orig) {
        // table structure is read only, does not have to be copied
        this.islandMultimap = orig.islandMultimap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> new ArrayList<>(e.getValue())));
        this.fixedIslands = new Archipelago(orig.fixedIslands);
        this.fixedRows = new HashSet<>(orig.fixedRows);
        this.fixedVertices = new HashSet<>(orig.fixedVertices);
        this.table = orig.table;
    }

    /*
     * Return whether a coordinate overlaps with an already committed coordinate
     */
    public boolean doesCoordinateOverlapWithCommittedCoordinate(Coordinate coordinate) {
        return fixedRows.contains(coordinate.row) || //
            fixedVertices.contains(table.vertexAt(coordinate.row, coordinate.column));
    }

    /*
     * Return whether an island overlaps with an already committed island
     */
    public boolean isIslandPossibleCandidate(Island island) {
        for (Coordinate coordinate : island) {
            if (doesCoordinateOverlapWithCommittedCoordinate(coordinate)) return false;
        }
        return true;
    }

    /*
     * Commit an island in the match table
     * Island will be part of the final alignment
     */
    public void addIsland(Island isl) {
        if (LOG.isLoggable(Level.FINE)) {
            LOG.log(Level.FINE, "adding island: '{0}'", isl);
        }
        for (Coordinate coordinate : isl) {
            fixedRows.add(coordinate.row);
            fixedVertices.add(table.vertexAt(coordinate.row, coordinate.column));
        }
        fixedIslands.add(isl);
        islandMultimap.computeIfPresent(isl.size(), (s, i) -> {
            i.remove(isl);
            return (i.isEmpty() ? null : i);
        });
    }

    public boolean doesCandidateLayOnVectorOfCommittedIsland(Island island) {
        Coordinate leftEnd = island.getLeftEnd();
        return fixedIslands.getIslandVectors().contains(leftEnd.row - leftEnd.column);
    }

    public int size() {
        return fixedIslands.size();
    }

    public List getIslands() {
        return fixedIslands.getIslands();
    }

    public boolean containsCoordinate(int row, int column) {
        return fixedIslands.containsCoordinate(row, column);
    }

    /*
     * For all the possible islands of a certain size this method checks whether
     * they conflict with one of the previously committed islands. If so, the
     * possible island is removed from the multimap. Or in case of overlap, split
     * into a smaller island and then put in back into the map Note that this
     * method changes the possible islands multimap.
     */
    //TODO: the original Island object is modified here
    //TODO: That should not happen, if we want to build a decision tree.
    public void removeOrSplitImpossibleIslands(Integer islandSize, Map> islandMultimap) {
        Collection islandsToCheck = new ArrayList<>(islandMultimap.getOrDefault(islandSize, Collections.emptyList()));
        for (Island island : islandsToCheck) {
            if (!isIslandPossibleCandidate(island)) {
                islandMultimap.computeIfPresent(islandSize, (s, i) -> {
                    i.remove(island);
                    return (i.isEmpty() ? null : i);
                });
                removeConflictingEndCoordinates(island);
                if (island.size() > 0) {
                    islandMultimap.computeIfAbsent(island.size(), s -> new ArrayList<>()).add(island);
                }
            }
        }
    }

    private void removeConflictingEndCoordinates(Island island) {
        boolean goOn = true;
        while (goOn) {
            Coordinate leftEnd = island.getLeftEnd();
            if (doesCoordinateOverlapWithCommittedCoordinate(leftEnd)) {
                island.removeCoordinate(leftEnd);
                if (island.size() == 0) {
                    return;
                }
            } else {
                goOn = false;
            }
        }
        goOn = true;
        while (goOn) {
            Coordinate rightEnd = island.getRightEnd();
            if (doesCoordinateOverlapWithCommittedCoordinate(rightEnd)) {
                island.removeCoordinate(rightEnd);
                if (island.size() == 0) {
                    return;
                }
            } else {
                goOn = false;
            }
        }
    }

    public List getPossibleIslands() {
        List possibleIslands = new ArrayList<>();
        while (possibleIslands.isEmpty() && !islandMultimap.isEmpty()) {
            // find the maximum island size and traverse groups in descending order
            Integer max = Collections.max(islandMultimap.keySet());
            LOG.fine("Checking islands of size: " + max);
            // check the possible islands of a certain size against
            // the already committed islands.
            removeOrSplitImpossibleIslands(max, islandMultimap);
            possibleIslands = new ArrayList<>(islandMultimap.getOrDefault(max, Collections.emptyList()));
        }
        return possibleIslands;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy