package edu.kit.pse.ws2013.routekit.controllers;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.swing.JOptionPane;
import org.xml.sax.SAXException;
import edu.kit.pse.ws2013.routekit.map.StreetMap;
import edu.kit.pse.ws2013.routekit.models.ProfileMapCombination;
import edu.kit.pse.ws2013.routekit.models.ProgressReporter;
import edu.kit.pse.ws2013.routekit.models.ProgressReporter.CloseableTask;
import edu.kit.pse.ws2013.routekit.precalculation.MapImporter;
import edu.kit.pse.ws2013.routekit.precalculation.OSMMapImporter;
import edu.kit.pse.ws2013.routekit.precalculation.PreCalculator;
/**
* A collection of actions when managing maps and precalculations
*/
public class ManagementActions {
public static final ManagementActions noActions = new ManagementActions(
Collections.<FutureMap> emptySet(),
Collections.<StreetMap> emptySet(),
Collections.<ProfileMapCombination> emptySet(),
Collections.<ProfileMapCombination> emptySet());
private final static float FACTOR_DELETE_PRECALCULATION = 1;
private final static float FACTOR_DELETE_MAP = 1;
private final static float FACTOR_IMPORT_MAP = 100;
private final static float FACTOR_PERFORM_PRECALCULATION = 500;
/**
* Maps that need to be imported.
*/
private final Set<FutureMap> newOrUpdatedMaps;
/**
* Maps that need to be deleted.
* <p>
* (Does <i>not</i> contain updated maps – if you want to know which
* precalculations to delete, use {@link #deletedPrecalculations}.)
*/
private final Set<StreetMap> deletedMaps;
/**
* Precalculations that need to be deleted.
*/
private final Set<ProfileMapCombination> deletedPrecalculations;
/**
* Precalculations that need to be performed.
*/
private final Set<ProfileMapCombination> newPrecalculations;
public ManagementActions(Set<FutureMap> newOrUpdatedMaps,
Set<StreetMap> deletedMaps,
Set<ProfileMapCombination> deletedPrecalculations,
Set<ProfileMapCombination> newPrecalculations) {
this.newOrUpdatedMaps = newOrUpdatedMaps;
this.deletedMaps = deletedMaps;
this.deletedPrecalculations = deletedPrecalculations;
this.newPrecalculations = newPrecalculations;
}
/**
* @return Maps that need to be imported.
*/
public Set<FutureMap> getNewOrUpdatedMaps() {
return newOrUpdatedMaps;
}
/**
* @return Maps that need to be deleted.
* <p>
* (Does <i>not</i> contain updated maps – if you want to know which
* precalculations to delete, use {@link #deletedPrecalculations}.
*/
public Set<StreetMap> getDeletedMaps() {
return deletedMaps;
}
/**
* @return Precalculations that need to be deleted.
*/
public Set<ProfileMapCombination> getDeletedPrecalculations() {
return deletedPrecalculations;
}
/**
* @return Precalculations that need to be performed.
*/
public Set<ProfileMapCombination> getNewPrecalculations() {
return newPrecalculations;
}
/**
* Execute the changes.
*
* @param selectedMap
* A special map to look for; when a map with this name is
* imported/updated, the updated map is remembered and later
* returned.
* @param reporter
* A {@link ProgressReporter} to report progress to.
* @return The last imported/updated map with {@code selectedMap}’s name, or
* {@code selectedMap} if no such map was updated/imported.
*/
public StreetMap execute(StreetMap selectedMap, ProgressReporter reporter) {
MapManager mapManager = MapManager.getInstance();
ProfileMapManager profileMapManager = ProfileMapManager.getInstance();
final float delPrecalcWeight = getDeletedPrecalculations().size()
* FACTOR_DELETE_PRECALCULATION;
final float delMapWeight = getDeletedMaps().size() * FACTOR_DELETE_MAP;
final float importWeight = getNewOrUpdatedMaps().size()
* FACTOR_IMPORT_MAP;
final float precalcWeight = getNewPrecalculations().size()
* FACTOR_PERFORM_PRECALCULATION;
final float totalWeight = delPrecalcWeight + delMapWeight
+ importWeight + precalcWeight;
reporter.setSubTasks(new float[] { delPrecalcWeight / totalWeight,
delMapWeight / totalWeight, importWeight / totalWeight,
precalcWeight / totalWeight });
reporter.pushTask("Lösche Vorberechnungen");
reporter.setSubTasks(getDeletedPrecalculations().size());
for (ProfileMapCombination precalculation : getDeletedPrecalculations()) {
reporter.pushTask("Lösche Vorberechnung '" + precalculation + "'");
profileMapManager.deletePrecalculation(precalculation,
!getDeletedMaps().contains(precalculation.getStreetMap()));
reporter.popTask();
}
reporter.nextTask("Lösche Karten");
reporter.setSubTasks(getDeletedMaps().size());
for (StreetMap map : getDeletedMaps()) {
reporter.pushTask("Lösche Karte '" + map.getName() + "'");
mapManager.deleteMap(map);
reporter.popTask();
}
reporter.popTask();
MapImporter importer = new OSMMapImporter();
reporter.pushTask("Importiere und speichere Karten");
reporter.setSubTasks(getNewOrUpdatedMaps().size());
/**
* Maps FutureMaps to actual imported StreetMaps.
*/
Map<StreetMap, StreetMap> importedMaps = new HashMap<>();
Set<StreetMap> failedMaps = new HashSet<>();
for (FutureMap map : getNewOrUpdatedMaps()) {
try (CloseableTask task = reporter
.openTask("Importiere und speichere Karte '"
+ map.getName() + "'")) {
reporter.setSubTasks(new float[] { .9f, .1f });
StreetMap importedMap;
try (CloseableTask task2 = reporter
.openTask("Importiere Karte '" + map.getName() + "'")) {
importedMap = importer.importMap(map.getOsmFile(),
map.getName(), reporter);
if (importedMap.getEdgeBasedGraph().getNumberOfTurns() == 0) {
// Easter Egg-ish
JOptionPane.showMessageDialog(
MainController.getInstance().view,
"Your map is bad\nand you should feel bad");
System.exit(1337);
}
} catch (IOException | SAXException e) {
MainController.getInstance().view.textMessage(e
.getMessage());
e.printStackTrace();
failedMaps.add(map);
if (selectedMap != null
&& selectedMap.getName().equals(map.getName())) {
selectedMap = null;
}
continue;
}
try (CloseableTask task3 = reporter
.openTask("Speichere Karte '" + importedMap.getName()
+ "'")) {
mapManager.saveMap(importedMap);
} catch (IOException e) {
MainController.getInstance().view.textMessage(e
.getMessage());
e.printStackTrace();
failedMaps.add(map);
continue;
}
importedMaps.put(map, importedMap);
if (selectedMap != null
&& selectedMap.getName().equals(map.getName())) {
selectedMap = importedMap;
}
}
}
reporter.popTask();
PreCalculator calculator = new PreCalculator();
reporter.pushTask("Führe Vorberechnungen durch");
reporter.setSubTasks(getNewPrecalculations().size());
for (ProfileMapCombination combination : getNewPrecalculations()) {
if (failedMaps.contains(combination.getStreetMap())) {
continue;
}
reporter.pushTask("Führe Vorberechnung durch und speichere '"
+ combination.toString() + "'");
reporter.setSubTasks(new float[] { .95f, .05f });
if (importedMaps.containsKey(combination.getStreetMap())) {
combination = new ProfileMapCombination(
importedMaps.get(combination.getStreetMap()),
combination.getProfile());
}
try {
reporter.pushTask("Führe Vorberechnung durch für '"
+ combination + "'");
try {
calculator.doPrecalculation(combination, reporter);
} catch (Exception e) {
e.printStackTrace();
continue;
} finally {
reporter.popTask("Führe Vorberechnung durch für '"
+ combination + "'");
}
reporter.pushTask("Speichere '" + combination + "'");
try {
profileMapManager.savePrecalculation(combination);
} catch (Exception e) {
e.printStackTrace();
continue;
} finally {
reporter.popTask("Speichere '" + combination + "'");
}
} finally {
reporter.popTask("Führe Vorberechnung durch und speichere '"
+ combination.toString() + "'");
}
}
reporter.popTask();
return selectedMap;
}
}