package pipe.controllers.application; import pipe.actions.gui.PipeApplicationModel; import pipe.controllers.*; import pipe.gui.PetriNetTab; import pipe.historyActions.AnimationHistoryImpl; import uk.ac.imperial.pipe.animation.PetriNetAnimator; import uk.ac.imperial.pipe.models.manager.PetriNetManager; import uk.ac.imperial.pipe.models.manager.PetriNetManagerImpl; import uk.ac.imperial.pipe.models.petrinet.*; import uk.ac.imperial.pipe.parsers.UnparsableException; import javax.swing.event.UndoableEditListener; import javax.xml.bind.JAXBException; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.*; /** * Pipes main application controller. * It houses the Petri net controllers of open tabs and is responsible for the creation of Petri nets */ public class PipeApplicationController { /** * Controllers for each tab */ private final Map<PetriNetTab, PetriNetController> netControllers = new HashMap<>(); /** * Main PIPE application model */ private final PipeApplicationModel applicationModel; /** * Manages creation/deletion of Petri net models */ private final PetriNetManager manager = new PetriNetManagerImpl(); /** * The current tab displayed in the view */ private PetriNetTab activeTab; /** * Constructor * @param applicationModel Main PIPE application model */ public PipeApplicationController(PipeApplicationModel applicationModel) { this.applicationModel = applicationModel; } /** * * @param listener to listen for change events in the petri net manager */ public void registerToManager(PropertyChangeListener listener) { manager.addPropertyChangeListener(listener); } /** * Creates an empty Petri net with a default token */ public void createEmptyPetriNet() { manager.createNewPetriNet(); } /** * Register the tab to the Petri net * @param net Petri net * @param tab tab which houses the graphical petri net components * @param historyObserver listener for stepback/forward events in animation * @param undoListener listener for undo/redo events * @param zoomListener listener for zoom events */ //TODO: THIS IS RATHER UGLY, too many params but better than what was here before public void registerTab(PetriNet net, PetriNetTab tab, Observer historyObserver, UndoableEditListener undoListener, PropertyChangeListener zoomListener) { AnimationHistoryImpl animationHistory = new AnimationHistoryImpl(); animationHistory.addObserver(historyObserver); GUIAnimator animator = new GUIAnimator(new PetriNetAnimator(net), animationHistory, this); CopyPasteManager copyPasteManager = new CopyPasteManager(undoListener, tab, net, this); ZoomController zoomController = new ZoomController(100); tab.addZoomListener(zoomController); PetriNetController petriNetController = new PetriNetController(net, undoListener, animator, copyPasteManager, zoomController, tab); netControllers.put(tab, petriNetController); tab.updatePreferredSize(); PropertyChangeListener changeListener = new PetriNetChangeListener(applicationModel, tab, petriNetController); net.addPropertyChangeListener(changeListener); setActiveTab(tab); initialiseNet(net, changeListener); } /** * * @param tab the active tab - this is the tab that is currently being displayed in the view */ public void setActiveTab(PetriNetTab tab) { this.activeTab = tab; } /** * This is a little hacky, I'm not sure how to make this better when it's so late * If a better implementation is clear please re-write * <p/> * This method invokes the change listener which will create the view objects on the * petri net tab * * @param propertyChangeListener */ private void initialiseNet(PetriNet net, PropertyChangeListener propertyChangeListener) { for (Token token : net.getTokens()) { PropertyChangeEvent changeEvent = new PropertyChangeEvent(net, PetriNet.NEW_TOKEN_CHANGE_MESSAGE, null, token); propertyChangeListener.propertyChange(changeEvent); } for (Place place : net.getPlaces()) { PropertyChangeEvent changeEvent = new PropertyChangeEvent(net, PetriNet.NEW_PLACE_CHANGE_MESSAGE, null, place); propertyChangeListener.propertyChange(changeEvent); } for (Transition transition : net.getTransitions()) { PropertyChangeEvent changeEvent = new PropertyChangeEvent(net, PetriNet.NEW_TRANSITION_CHANGE_MESSAGE, null, transition); propertyChangeListener.propertyChange(changeEvent); } for (Arc<? extends Connectable, ? extends Connectable> arc : net.getArcs()) { PropertyChangeEvent changeEvent = new PropertyChangeEvent(net, PetriNet.NEW_ARC_CHANGE_MESSAGE, null, arc); propertyChangeListener.propertyChange(changeEvent); } for (Annotation annotation : net.getAnnotations()) { PropertyChangeEvent changeEvent = new PropertyChangeEvent(net, PetriNet.NEW_ANNOTATION_CHANGE_MESSAGE, null, annotation); propertyChangeListener.propertyChange(changeEvent); } for (RateParameter rateParameter : net.getRateParameters()) { PropertyChangeEvent changeEvent = new PropertyChangeEvent(net, PetriNet.NEW_RATE_PARAMETER_CHANGE_MESSAGE, null, rateParameter); propertyChangeListener.propertyChange(changeEvent); } } /** * Loads and creates a Petri net located at the given file * @param file location of the XML file which contains a PNML representation of a Petri net * @throws UnparsableException if the file cannot be parsed */ public void createNewTabFromFile(File file) throws UnparsableException { try { manager.createFromFile(file); } catch (FileNotFoundException | JAXBException e) { throw new UnparsableException("Could not initialise Petri net reader!", e); } } /** * Save the currently displayed petri net to the specified file * @param outFile location to save the Petri net * @throws ParserConfigurationException configuration error * @throws TransformerException transformer error * @throws IllegalAccessException illegal access * @throws NoSuchMethodException method not found * @throws InvocationTargetException invocation error */ public void saveAsCurrentPetriNet(File outFile) throws ParserConfigurationException, TransformerException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { PetriNetController petriNetController = getActivePetriNetController(); PetriNet petriNet = petriNetController.getPetriNet(); try { manager.savePetriNet(petriNet, outFile); } catch (JAXBException | IOException e) { throw new RuntimeException("Failed to write!", e); } petriNetController.save(); } /** * * @return the active Petri net controller */ public PetriNetController getActivePetriNetController() { return netControllers.get(activeTab); } /** * @return true if the current petri net has changed */ public boolean hasCurrentPetriNetChanged() { PetriNetController activeController = getActivePetriNetController(); return activeController != null && activeController.hasChanged(); } public boolean anyNetsChanged() { return !getNetsChanged().isEmpty(); } /** * @return the names of the petri nets that have changed */ public Set<String> getNetsChanged() { Set<String> changed = new HashSet<>(); for (PetriNetController controller : netControllers.values()) { if (controller.hasChanged()) { changed.add(controller.getPetriNet().getNameValue()); } } return changed; } /** * Removes the active tab from display if it exists. * Note active tab must be removed from netControllers before the petri net is removed * from the manager because the manager will fire a message which causes the active tab * to be swapped to the new open tab */ public void removeActiveTab() { if (activeTab != null) { PetriNetController controller = netControllers.get(activeTab); netControllers.remove(activeTab); PetriNet petriNet = controller.getPetriNet(); manager.remove(petriNet); } } /** * * @return the current active tab */ public PetriNetTab getActiveTab() { return activeTab; } }