diff --git a/src/main/java/org/mastodon/mamut/classification/ClassifyLineagesController.java b/src/main/java/org/mastodon/mamut/classification/ClassifyLineagesController.java index b17eda7d1..b8cd99106 100644 --- a/src/main/java/org/mastodon/mamut/classification/ClassifyLineagesController.java +++ b/src/main/java/org/mastodon/mamut/classification/ClassifyLineagesController.java @@ -28,13 +28,13 @@ */ package org.mastodon.mamut.classification; -import mpicbg.spim.data.SpimDataException; import org.apache.commons.lang3.tuple.Pair; import org.mastodon.collection.RefSet; import org.mastodon.graph.algorithm.traversal.DepthFirstIterator; import org.mastodon.mamut.ProjectModel; import org.mastodon.mamut.classification.config.ClusteringMethod; import org.mastodon.mamut.classification.config.SimilarityMeasure; +import org.mastodon.mamut.classification.multiproject.ExternalProjects; import org.mastodon.mamut.classification.util.Classification; import org.mastodon.mamut.classification.config.CropCriteria; import org.mastodon.mamut.classification.ui.DendrogramView; @@ -60,12 +60,9 @@ import java.io.IOException; import java.lang.invoke.MethodHandles; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; @@ -90,8 +87,6 @@ public class ClassifyLineagesController private final PrefService prefs; - private final MastodonProjectService projectService; - private SimilarityMeasure similarityMeasure = SimilarityMeasure.NORMALIZED_ZHANG_DIFFERENCE; private ClusteringMethod clusteringMethod = ClusteringMethod.AVERAGE_LINKAGE; @@ -106,12 +101,10 @@ public class ClassifyLineagesController private int minCellDivisions; - private final Map< File, ProjectSession > externalProjects; - - private final Map< File, String > failingExternalProjects; - private boolean showDendrogram; + private final ExternalProjects externalProjects; + private boolean addTagSetToExternalProjects; private boolean running = false; @@ -137,9 +130,7 @@ public ClassifyLineagesController( final ProjectModel referenceProjectModel, fin this.referenceProjectModel = referenceProjectModel; this.referenceModel = referenceProjectModel.getModel(); this.prefs = prefs; - this.projectService = projectService; - this.externalProjects = new HashMap<>(); - this.failingExternalProjects = new HashMap<>(); + this.externalProjects = new ExternalProjects( projectService ); } /** @@ -197,10 +188,10 @@ private Classification< BranchSpotTree > classifyUsingExternalProjects( final Li for ( int i = 1; i < roots.size(); i++ ) { averageClassification = classifyLineageTrees( roots.get( i ), distances ); - for ( ProjectSession projectSession : externalProjects.values() ) + for ( Map.Entry< File, ProjectSession > externalProject : externalProjects.getProjects().entrySet() ) { - ProjectModel projectModel = projectSession.getProjectModel(); - File file = projectSession.getFile(); + ProjectModel projectModel = externalProject.getValue().getProjectModel(); + File file = externalProject.getKey(); branchSpotProvider = branchSpotTree -> projectModel.getModel().getBranchGraph().vertices().stream() .filter( ( branchSpot -> branchSpot.getFirstLabel().equals( branchSpotTree.getName() ) ) ) .findFirst().orElse( null ); @@ -233,9 +224,9 @@ private Pair< List< List< BranchSpotTree > >, double[][] > getRootsAndDistanceMa keepCommonRootsAndSort( roots, commonRootNames ); treeMatrix.add( roots ); - for ( ProjectSession projectSession : externalProjects.values() ) + for ( ProjectModel projectModel : externalProjects.getProjectModels() ) { - List< BranchSpotTree > externalRoots = getRoots( projectSession.getProjectModel() ); + List< BranchSpotTree > externalRoots = getRoots( projectModel ); keepCommonRootsAndSort( externalRoots, commonRootNames ); treeMatrix.add( externalRoots ); } @@ -245,9 +236,9 @@ private Pair< List< List< BranchSpotTree > >, double[][] > getRootsAndDistanceMa private List< String > findCommonRootNames() { Set< String > commonRootNames = extractRootNamesFromProjectModel( referenceProjectModel ); - for ( ProjectSession projectSession : externalProjects.values() ) + for ( ProjectModel projectModel : externalProjects.getProjectModels() ) { - Set< String > rootNames = extractRootNamesFromProjectModel( projectSession.getProjectModel() ); + Set< String > rootNames = extractRootNamesFromProjectModel( projectModel ); commonRootNames.retainAll( rootNames ); } List< String > commonRootNamesList = new ArrayList<>( commonRootNames ); @@ -430,68 +421,7 @@ public void setShowDendrogram( final boolean showDendrogram ) public void setExternalProjects( final File[] projects, final boolean addTagSetToExternalProjects ) { this.addTagSetToExternalProjects = addTagSetToExternalProjects; - List< File > projectsList = projects == null ? Collections.emptyList() : Arrays.asList( projects ); - removeProjects( projectsList ); - cleanUpFailingProjects( projectsList ); - addProjects( projects ); - } - - /** - * Remove files from the externalProjects map that are not in the projects list - */ - private void removeProjects( final List< File > projectsList ) - { - Iterator< Map.Entry< File, ProjectSession > > iterator = externalProjects.entrySet().iterator(); - while ( iterator.hasNext() ) - { - Map.Entry< File, ProjectSession > entry = iterator.next(); - File file = entry.getKey(); - if ( !projectsList.contains( file ) ) - { - ProjectSession projectSession = entry.getValue(); - projectSession.close(); - iterator.remove(); - } - } - } - - /** - * Remove files from the failingExternalProjects map that are not in the projects list - */ - private void cleanUpFailingProjects( final List< File > projectsList ) - { - for ( Map.Entry< File, String > entry : failingExternalProjects.entrySet() ) - { - File file = entry.getKey(); - if ( !projectsList.contains( file ) ) - failingExternalProjects.remove( file ); - } - } - - /** - * Add files from projects to the map if they are not already present - */ - private void addProjects( final File[] projects ) - { - if ( projects == null ) - return; - for ( File file : projects ) - { - if ( !externalProjects.containsKey( file ) ) - { - try - { - externalProjects.put( file, projectService.createSession( file ) ); - failingExternalProjects.remove( file ); - } - catch ( SpimDataException | IOException | RuntimeException e ) - { - failingExternalProjects.put( file, - "Could not read project from file " + file.getAbsolutePath() + ".
Error: " + e.getMessage() ); - logger.warn( "Could not read project from file {}. Error: {}", file.getAbsolutePath(), e.getMessage() ); - } - } - } + externalProjects.setProjects( projects ); } public List< String > getFeedback() @@ -514,7 +444,7 @@ public List< String > getFeedback() } if ( cropCriterion.equals( CropCriteria.NUMBER_OF_SPOTS ) ) feedback.addAll( checkNumberOfSpots() ); - feedback.addAll( failingExternalProjects.values() ); + feedback.addAll( externalProjects.getFailingProjectMessages() ); return feedback; } @@ -544,8 +474,7 @@ private List< String > checkNumberOfSpots() { List< String > feedback = new ArrayList<>(); Set< ProjectModel > allModels = new HashSet<>( Collections.singletonList( referenceProjectModel ) ); - for ( ProjectSession projectSession : externalProjects.values() ) - allModels.add( projectSession.getProjectModel() ); + allModels.addAll( externalProjects.getProjectModels() ); for ( ProjectModel projectModel : allModels ) { Model model = projectModel.getModel(); @@ -576,7 +505,6 @@ private List< String > checkNumberOfSpots() public void close() { - for ( ProjectSession projectSession : externalProjects.values() ) - projectSession.close(); + externalProjects.close(); } } diff --git a/src/main/java/org/mastodon/mamut/classification/multiproject/ExternalProjects.java b/src/main/java/org/mastodon/mamut/classification/multiproject/ExternalProjects.java new file mode 100644 index 000000000..d9098075e --- /dev/null +++ b/src/main/java/org/mastodon/mamut/classification/multiproject/ExternalProjects.java @@ -0,0 +1,167 @@ +package org.mastodon.mamut.classification.multiproject; + +import mpicbg.spim.data.SpimDataException; +import org.mastodon.mamut.ProjectModel; +import org.mastodon.mamut.util.MastodonProjectService; +import org.mastodon.mamut.util.ProjectSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * A collection of external projects. + *
+ * This class manages a collection of external projects. It holds a mapping of project files to project sessions and a mapping of projects that failed to be loaded and the reason why they failed to load. + */ +public class ExternalProjects implements AutoCloseable +{ + private static final Logger logger = LoggerFactory.getLogger( MethodHandles.lookup().lookupClass() ); + + private final MastodonProjectService projectService; + + private final Map< File, ProjectSession > projectSessions; + + private final Map< File, String > failingProjects; + + public ExternalProjects( final MastodonProjectService projectService ) + { + this.projectService = projectService; + this.projectSessions = new HashMap<>(); + this.failingProjects = new HashMap<>(); + } + + /** + * Gets all project models of the external projects + * @return the project models + */ + public Collection< ProjectModel > getProjectModels() + { + return projectSessions.values().stream().map( ProjectSession::getProjectModel ).collect( Collectors.toList() ); + } + + /** + * Gets a mapping of external project files to their project sessions + * @return the mapping + */ + public Map< File, ProjectSession > getProjects() + { + return projectSessions; + } + + /** + * Get the number of external projects + * @return the number of external projects + */ + public int size() + { + return projectSessions.size(); + } + + /** + * Check if the external projects is empty + * @return {@code true} if the external projects is empty, {@code false} otherwise + */ + public boolean isEmpty() + { + return projectSessions.isEmpty(); + } + + /** + * Get a Collection failing projects and the reason why they failed to load. + * @return the failing projects + */ + public Collection< String > getFailingProjectMessages() + { + return failingProjects.values(); + } + + /** + * Set the external projects + * @param projects the external projects + */ + public void setProjects( final File[] projects ) + { + List< File > projectsList = projects == null ? Collections.emptyList() : Arrays.asList( projects ); + removeProjects( projectsList ); + cleanUpFailingProjects( projectsList ); + addProjects( projects ); + logger.debug( "Set {} projects. Active project sessions: {}.", projectsList.size(), projectService.activeSessions() ); + } + + /** + * Remove files from the externalProjects map that are not in the projects list + */ + private void removeProjects( final List< File > projectsList ) + { + Iterator< Map.Entry< File, ProjectSession > > iterator = projectSessions.entrySet().iterator(); + while ( iterator.hasNext() ) + { + Map.Entry< File, ProjectSession > entry = iterator.next(); + File file = entry.getKey(); + if ( !projectsList.contains( file ) ) + { + ProjectSession projectSession = entry.getValue(); + projectSession.close(); + iterator.remove(); + } + } + } + + /** + * Remove files from the failingExternalProjects map that are not in the projects list + */ + private void cleanUpFailingProjects( final List< File > projectsList ) + { + for ( Map.Entry< File, String > entry : failingProjects.entrySet() ) + { + File file = entry.getKey(); + if ( !projectsList.contains( file ) ) + failingProjects.remove( file ); + } + } + + /** + * Add files from projects to the map if they are not already present + */ + private void addProjects( final File[] files ) + { + if ( files == null ) + return; + for ( File file : files ) + { + if ( !projectSessions.containsKey( file ) ) + { + try + { + projectSessions.put( file, projectService.createSession( file ) ); + failingProjects.remove( file ); + } + catch ( SpimDataException | IOException | RuntimeException e ) + { + failingProjects.put( file, + "Could not read project from file " + file.getAbsolutePath() + ".
Error: " + e.getMessage() ); + logger.warn( "Could not read project from file {}. Error: {}", file.getAbsolutePath(), e.getMessage() ); + } + } + } + } + + @Override + public void close() + { + for ( ProjectSession projectSession : projectSessions.values() ) + projectSession.close(); + logger.debug( "Remaining active project sessions: {}.", projectService.activeSessions() ); + } +} diff --git a/src/main/java/org/mastodon/mamut/util/MastodonProjectService.java b/src/main/java/org/mastodon/mamut/util/MastodonProjectService.java index d806acbe2..1899a1748 100644 --- a/src/main/java/org/mastodon/mamut/util/MastodonProjectService.java +++ b/src/main/java/org/mastodon/mamut/util/MastodonProjectService.java @@ -68,7 +68,7 @@ public ProjectSession createSession( final File file ) throws SpimDataException, if ( projectModels.containsKey( file ) ) { ProjectModel projectModel = projectModels.get( file ); - ProjectSession projectSession = new ProjectSession( file, projectModel, this ); + ProjectSession projectSession = new ProjectSession( projectModel, this ); sessions.get( projectModel ).add( projectSession ); return projectSession; } @@ -78,7 +78,7 @@ public ProjectSession createSession( final File file ) throws SpimDataException, ProjectModel projectModel = ProjectLoader.open( file.getAbsolutePath(), getContext(), false, true ); logger.debug( "Loaded project from file: {} in {} ms", file.getAbsolutePath(), System.currentTimeMillis() - start ); - ProjectSession projectSession = new ProjectSession( file, projectModel, this ); + ProjectSession projectSession = new ProjectSession( projectModel, this ); projectModels.put( file, projectModel ); List< ProjectSession > projectSessionList = new ArrayList<>(); projectSessionList.add( projectSession ); diff --git a/src/main/java/org/mastodon/mamut/util/ProjectSession.java b/src/main/java/org/mastodon/mamut/util/ProjectSession.java index cb8af2ccd..2e09c7e31 100644 --- a/src/main/java/org/mastodon/mamut/util/ProjectSession.java +++ b/src/main/java/org/mastodon/mamut/util/ProjectSession.java @@ -15,19 +15,15 @@ public class ProjectSession implements AutoCloseable { private final MastodonProjectService service; - private final File file; - private final ProjectModel projectModel; /** * Creates a new project session. - * @param file the file the project was loaded from. * @param projectModel the project model. * @param service the service that created this session. */ - public ProjectSession( final File file, final ProjectModel projectModel, final MastodonProjectService service ) + public ProjectSession( final ProjectModel projectModel, final MastodonProjectService service ) { - this.file = file; this.projectModel = projectModel; this.service = service; } @@ -49,13 +45,4 @@ public ProjectModel getProjectModel() { return projectModel; } - - /** - * Gets the file the project was loaded from. - * @return the file. - */ - public File getFile() - { - return file; - } } diff --git a/src/test/java/org/mastodon/mamut/util/MastodonProjectServiceTest.java b/src/test/java/org/mastodon/mamut/util/MastodonProjectServiceTest.java index 6db12eef6..3a3f64a01 100644 --- a/src/test/java/org/mastodon/mamut/util/MastodonProjectServiceTest.java +++ b/src/test/java/org/mastodon/mamut/util/MastodonProjectServiceTest.java @@ -39,9 +39,6 @@ void test() throws IOException, SpimDataException assertEquals( 1, service.activeSessions() ); projectSession3.close(); assertEquals( 0, service.activeSessions() ); - assertEquals( mastodonFile1, projectSession1.getFile() ); - assertEquals( mastodonFile1, projectSession2.getFile() ); - assertEquals( mastodonFile2, projectSession3.getFile() ); } } }