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

de.uni.freiburg.iig.telematik.sepia.petrinet.abstr.AbstractPetriNet Maven / Gradle / Ivy

package de.uni.freiburg.iig.telematik.sepia.petrinet.abstr;

import de.invation.code.toval.misc.NamedComponent;
import java.io.Serializable;
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 de.invation.code.toval.validate.ParameterException;
import de.invation.code.toval.validate.ParameterException.ErrorCode;
import de.invation.code.toval.validate.Validate;
import de.uni.freiburg.iig.telematik.jagal.traverse.Traversable;
import de.uni.freiburg.iig.telematik.sepia.event.CapacityEvent;
import de.uni.freiburg.iig.telematik.sepia.event.FlowRelationListener;
import de.uni.freiburg.iig.telematik.sepia.event.MarkingChangeEvent;
import de.uni.freiburg.iig.telematik.sepia.event.PNMarkingListener;
import de.uni.freiburg.iig.telematik.sepia.event.PNMarkingListenerSupport;
import de.uni.freiburg.iig.telematik.sepia.event.PNStructureListener;
import de.uni.freiburg.iig.telematik.sepia.event.PNStructureListenerSupport;
import de.uni.freiburg.iig.telematik.sepia.event.PlaceChangeEvent;
import de.uni.freiburg.iig.telematik.sepia.event.PlaceListener;
import de.uni.freiburg.iig.telematik.sepia.event.RelationChangeEvent;
import de.uni.freiburg.iig.telematik.sepia.event.RelationConstraintEvent;
import de.uni.freiburg.iig.telematik.sepia.event.TokenEvent;
import de.uni.freiburg.iig.telematik.sepia.event.TokenListener;
import de.uni.freiburg.iig.telematik.sepia.event.TransitionChangeEvent;
import de.uni.freiburg.iig.telematik.sepia.event.TransitionEvent;
import de.uni.freiburg.iig.telematik.sepia.event.TransitionListener;
import de.uni.freiburg.iig.telematik.sepia.exception.PNException;
import de.uni.freiburg.iig.telematik.sepia.petrinet.NetType;
import de.uni.freiburg.iig.telematik.sepia.petrinet.PNComplexity;
import de.uni.freiburg.iig.telematik.sepia.util.PNUtils;

/**
 * Abstract class for defining Petri nets and their properties.
*
* It contains major properties like the places, transitions and relations of a * Petri net and provides methods for adding/removing them.
*
* It keeps track of the nets' state by remembering the marking of the net which * describes the number and kind of tokens contained in net places. The net * state changes when transitions fire. Using listeners, the net reacts on * changes of transitions and places that belong to the net. *
* It also keeps track of enabled transitions, transition and place * sources/drains. A transition is enabled if all its precondition hold.
* Preconditions are defined in the sense of constraints on the number and * kind
* of tokens contained in places that have outgoing relations to the * transition.
* In case an enabled transition fires, it removes tokens from incoming places * and puts tokens into outgoing places. *
* This class allows to fire an enabled transition.
* In case there is more than one transition, the FlowControl determines the * transition to fire. * * @author Thomas Stocker * * @param

Type of Petri net places * @param Type of Petri net transitions * @param Type of Petri net relations * @param Type of Petri net markings * @param Type of Petri net place states */ public abstract class AbstractPetriNet< P extends AbstractPlace, T extends AbstractTransition, F extends AbstractFlowRelation, M extends AbstractMarking, S extends Object> implements TransitionListener>, TokenListener>, Traversable>, FlowRelationListener>, PlaceListener>, PNStructureListener, PNMarkingListener, Serializable, NamedComponent { private static final long serialVersionUID = 7324151598039390349L; /** * Name of the Petri net. */ protected String name = "PetriNet"; /** * Map that contains Petri net places indexed with their names. */ protected Map places = new HashMap<>(); /** * Map that contains Petri net places without incoming relations. */ protected Map sourcePlaces = new HashMap<>(); /** * Map that contains Petri net places without outgoing relations. */ protected Map drainPlaces = new HashMap<>(); /** * Map that contains Petri net transitions indexed with their names. */ protected Map transitions = new HashMap<>(); /** * Map that contains Petri net transitions without incoming relations. */ protected Map sourceTransitions = new HashMap<>(); /** * Map that contains Petri net transitions without outgoing relations. */ protected Map drainTransitions = new HashMap<>(); /** * Map that contains Petri net relations indexed with their names. */ protected Map relations = new HashMap<>(); /** * A list that contains all enabled transitions.
*/ protected List enabledTransitions = new ArrayList<>(); /** * The last fired transition. */ protected T lastFiredTransition = null; /** * The initial marking of the Petri net.
* This marking defines the initial state of a Petri net, before the firing * of any transitions. */ protected M initialMarking = null; /** * The actual marking of the Petri net.
* This marking changes whenever transitions fire and tokens are added to or * removed from net places. */ protected M marking = null; protected PNStructureListenerSupport structureListenerSupport = new PNStructureListenerSupport<>(); protected PNMarkingListenerSupport markingListenerSupport = new PNMarkingListenerSupport<>(); //------- Constructors -------------------------------------------------------------------------- /** * Creates a new Petri net, using a DefaultFlow as flow control.
* The next enabled transition is chosen randomly. */ public AbstractPetriNet() { initialize(); initialMarking = createNewMarking(); marking = createNewMarking(); structureListenerSupport.addListener(this); } /** * Creates a new Petri net using a DefaultFlow as flow control.
* The next enabled transition is chosen randomly. * * @param places Names of Petri net places to add. * @param transitions Names of Petri net transition to add. * @throws ParameterException If some parameters are null or * contain null-values. */ public AbstractPetriNet(Set places, Set transitions) { this(); addTransitions(transitions); addPlaces(places); } /** * Initialization method for basic properties and fields.
* Called by default constructor. */ protected void initialize() { } ; //------- Basic properties ---------------------------------------------------------------------- /** * Returns the name of the Petri net. * @return The name of the Petri net. */ @Override public final String getName() { return name; } /** * Sets the name of the Petri net to the given name. * * @param name New name for the Petri net. * @throws ParameterException If the given name is null. */ @Override public final void setName(String name) { Validate.notNull(name); PNUtils.validateElementName(name); this.name = name; } /** * Checks if the Petri net has enabled transitions that are ready to fire. * * @return true if the net has enabled transitions;
* false otherwise. */ public boolean hasEnabledTransitions() { return enabledTransitions.size() > 0; } /** * Returns a list of all enabled transitions. * * @return A list of enabled transitions. */ public List getEnabledTransitions() { return Collections.unmodifiableList(enabledTransitions); } /** * Returns the last fired transition.
* * @return The last fired transition or null if no transition * was fired so far. */ public T getLastFiredTransition() { return lastFiredTransition; } public abstract NetType getNetType(); public boolean isEmpty() { return places.isEmpty() && transitions.isEmpty() && relations.isEmpty(); } public abstract Class getMarkingGraphClass(); //------- Transitions ------------------------------------------------------------------------ /** * Returns the Petri net transitions. * * @return A collection of Petri net transitions. */ public Collection getTransitions() { return Collections.unmodifiableCollection(transitions.values()); } public int getTransitionCount() { return transitions.size(); } public Collection getTransitions(boolean includeSilentTransitions) { if (includeSilentTransitions) { return getTransitions(); } List result = new ArrayList<>(getTransitions()); result.removeAll(getSilentTransitions()); return result; } public Collection getSilentTransitions() { List result = new ArrayList<>(); for (T transition : transitions.values()) { if (transition.isSilent()) { result.add(transition); } } return result; } /** * Returns the Petri net source transitions. * * @return A collection of Petri net transitions. */ public Collection getSourceTransitions() { return Collections.unmodifiableCollection(sourceTransitions.values()); } /** * Returns the Petri net drain transitions. * * @return A collection of Petri net transitions. */ public Collection getDrainTransitions() { return Collections.unmodifiableCollection(drainTransitions.values()); } /** * Returns the transition with the given name. * * @param transitionName The name of the desired transition. * @return The Petri net transition with the given name
* or null if the net does not contain a transition with the * given name. */ public T getTransition(String transitionName) { return transitions.get(transitionName); } /** * Returns all transitions with the given label. * * @param label The label of all desired transitions. * @return A set of all transitions with the given label. */ public Set getTransitions(String label) { Set result = new HashSet<>(); for (T transition : transitions.values()) { if (transition.getLabel().equals(label)) { result.add(transition); } } return result; } /** * Adds transitions with the given names to the Petri net.
* Transitions names have to be unique. In case the net already contains * transitions with given names, less transitions than the given number of * arguments may be added to the Petri net.
* This method calls {@link #addTransition(String)} for each transition * name. * * @param transitionNames Names for the Petri net transitions. * @return true if at least one transition was successfully * added;
* false otherwise. * @throws ParameterException If the set of transition names is * null or some transition names are null. * @see #addTransition(String) */ public final boolean addTransitions(Collection transitionNames) { return addTransitions(transitionNames, true); } /** * Adds transitions with the given names to the Petri net.
* Transitions names have to be unique. In case the net already contains * transitions with given names, less transitions than the given number of * arguments may be added to the Petri net.
* This method calls {@link #addTransition(String)} for each transition * name. * * @param transitionNames Names for the Petri net transitions. * @param notifyListeners Set true if listeners should be * notified. * @return true if at least one transition was successfully * added;
* false otherwise. * @throws ParameterException If the set of transition names is * null or some transition names are null. * @see #addTransition(String) */ public final boolean addTransitions(Collection transitionNames, boolean notifyListeners) { Validate.notNull(transitionNames); boolean updated = false; for (String transitionName : transitionNames) { if (addTransition(transitionName, notifyListeners)) { updated = true; } } return updated; } /** * Adds a transition with the given name to the Petri net.
* Transitions names have to be unique. In case the net already contains a * transition with the given name, no transition is added to the net. * * @param transitionName The name for the Petri net transition. * @return true if the transition was successfully added to the * net;
* false otherwise. * @throws ParameterException If the transition name is null. */ public boolean addTransition(String transitionName) { return addTransition(transitionName, true); } /** * Adds a transition with the given name to the Petri net.
* Transitions names have to be unique. In case the net already contains a * transition with the given name, no transition is added to the net. * * @param transitionName The name for the Petri net transition. * @param notifyListeners Set true if listeners should be * notified. * @return true if the transition was successfully added to the * net;
* false otherwise. * @throws ParameterException If the transition name is null. */ public boolean addTransition(String transitionName, boolean notifyListeners) { return addTransition(transitionName, transitionName, notifyListeners); } /** * Adds a transition with the given name and label to the Petri net.
* Transitions names have to be unique. In case the net already contains a * transition with the given name, no transition is added to the net. * * @param transitionName The name for the Petri net transition. * @param transitionLabel the label for the Petri net transition. * @return true if the transition was successfully added to the * net;
* false otherwise. * @throws ParameterException If the transition name is null. */ public boolean addTransition(String transitionName, String transitionLabel) { return addTransition(transitionName, transitionLabel, true); } /** * Adds a transition with the given name and label to the Petri net.
* Transitions names have to be unique. In case the net already contains a * transition with the given name, no transition is added to the net. * * @param transitionName The name for the Petri net transition. * @param transitionLabel the label for the Petri net transition. * @param notifyListeners Set true if listeners should be * notified. * @return true if the transition was successfully added to the * net;
* false otherwise. * @throws ParameterException If the transition name is null. */ public boolean addTransition(String transitionName, String transitionLabel, boolean notifyListeners) { if (containsTransition(transitionName)) { return false; } return addTransition(createNewTransition(transitionName, transitionLabel, false), notifyListeners); } /** * Adds a transition with the given name and silent-state to the Petri * net.
* Transitions names have to be unique. In case the net already contains a * transition with the given name, no transition is added to the net. * * @param transitionName The name for the Petri net transition. * @return true if the transition was successfully added to the * net;
* false otherwise. * @throws ParameterException If the transition name is null. */ public boolean addSilentTransition(String transitionName) { return addSilentTransition(transitionName, true); } /** * Adds a transition with the given name and silent-state to the Petri * net.
* Transitions names have to be unique. In case the net already contains a * transition with the given name, no transition is added to the net. * * @param transitionName The name for the Petri net transition. * @param notifyListeners Set true if listeners should be * notified. * @return true if the transition was successfully added to the * net;
* false otherwise. * @throws ParameterException If the transition name is null. */ public boolean addSilentTransition(String transitionName, boolean notifyListeners) { return addSilentTransition(transitionName, transitionName, notifyListeners); } /** * Adds a transition with the given name, label and silent-state to the * Petri net.
* Transitions names have to be unique. In case the net already contains a * transition with the given name, no transition is added to the net. * * @param transitionName The name for the Petri net transition. * @param transitionLabel the label for the Petri net transition. * @return true if the transition was successfully added to the * net;
* false otherwise. * @throws ParameterException If the transition name is null. */ public boolean addSilentTransition(String transitionName, String transitionLabel) { return addSilentTransition(transitionName, transitionLabel, true); } /** * Adds a transition with the given name, label and silent-state to the * Petri net.
* Transitions names have to be unique. In case the net already contains a * transition with the given name, no transition is added to the net. * * @param transitionName The name for the Petri net transition. * @param transitionLabel the label for the Petri net transition. * @param notifyListeners Set true if listeners should be * notified. * @return true if the transition was successfully added to the * net;
* false otherwise. * @throws ParameterException If the transition name is null. */ public boolean addSilentTransition(String transitionName, String transitionLabel, boolean notifyListeners) { if (containsTransition(transitionName)) { return false; } return addTransition(createNewTransition(transitionName, transitionLabel, true), notifyListeners); } /** * Protected method for adding a transition of type T to the * transition map.
* This method is called from {@link #addTransition(String)} which checks if * there already exists a transition with equal name before making the call. * * @param transition The Petri net transition to add. * @param notifyListeners Set true if listeners should be * notified. * @return true if transition has been added successvully. * @throws ParameterException If the given transition is null. */ protected boolean addTransition(T transition, boolean notifyListeners) { Validate.notNull(transition); if (containsTransition(transition)) { return false; } transitions.put(transition.getName(), transition); transition.addTransitionListener(this); enabledTransitions.add(transition); if (notifyListeners) { structureListenerSupport.notifyTransitionAdded(transition); } return true; } /** * Checks if the Petri net contains the given transition. * * @param transition The transition to check. * @return true if the net contains the transition;
* false otherwise. * @throws ParameterException If the given transition is null. */ protected boolean containsTransition(T transition) { return containsTransition(transition.getName()); } /** * Checks if the Petri net contains a transition with the given name. * * @param transitionName The name of the transition to check. * @return true if the net contains a transition with the given * name;
* false otherwise. */ public boolean containsTransition(String transitionName) { return transitions.keySet().contains(transitionName); } /** * Removes the transition with the given name from the Petri net.
* On removing a transition, all corresponding relations are removed as * well. * * @param transitionName The name of the place to remove * @return true if the transition was successfully removed from * the net;
* false otherwise. * @throws ParameterException If the given transition name is * null. */ public boolean removeTransition(String transitionName) { return removeTransition(transitionName, true); } /** * Removes the transition with the given name from the Petri net.
* On removing a transition, all corresponding relations are removed as * well. * * @param transitionName The name of the place to remove * @param notifyListeners Set true if listeners should be * notified. * @return true if the transition was successfully removed from * the net;
* false otherwise. * @throws ParameterException If the given transition name is * null. */ public boolean removeTransition(String transitionName, boolean notifyListeners) { if (!containsTransition(transitionName)) { return false; } return removeTransition(transitions.get(transitionName), notifyListeners); } protected boolean removeTransition(T transition, boolean notifyListeners) { if (!containsTransition(transition)) { return false; } int affectedRelations = transition.degree(); for (F relation : transition.getIncomingRelations()) { removeFlowRelation(relation, notifyListeners); } for (F relation : transition.getOutgoingRelations()) { removeFlowRelation(relation, notifyListeners); } transition.removeTransitionListener(this); enabledTransitions.remove(transition); transitions.remove(transition.getName()); sourceTransitions.remove(transition.getName()); drainTransitions.remove(transition.getName()); if (notifyListeners) { structureListenerSupport.notifyTransitionRemoved(transition, affectedRelations); } return true; } /** * Creates a new transition of type T with the given name.
* This method is abstract because only subclasses know the type * T of their transitions. * * @param name The name for the new transition. * @param label The label for the new transition. * @param isSilent Set true if new transition should be silent. * @return A new transition of type T with the given name. */ protected abstract T createNewTransition(String name, String label, boolean isSilent); //------- Places --------------------------------------------------------------------------------- /** * Returns all Petri net places. * * @return A collection of all Petri net places. */ public Collection

getPlaces() { return Collections.unmodifiableCollection(places.values()); } public int getPlaceCount() { return places.size(); } /** * Returns all Petri net source places. * * @return A collection of Petri net places. */ public Collection

getSourcePlaces() { return Collections.unmodifiableCollection(sourcePlaces.values()); } /** * Returns all Petri net drain places. * * @return A collection of Petri net places. */ public Collection

getDrainPlaces() { return Collections.unmodifiableCollection(drainPlaces.values()); } /** * Returns the net place with the given name. * * @param placeName The name of the desired place. * @return The Petri net place with the given name
* or null if the net does not contain a place with the given * name. */ public P getPlace(String placeName) { return places.get(placeName); } /** * Adds places with the given names to the Petri net.
* Place names have to be unique. In case the net already contains places * with given names, less places than the given number of arguments may be * added to the Petri net.
* This method calls {@link #addPlace(String)} for each place name. * * @param placeNames Names for the Petri net places to add. * @return true if at least one place was successfully * added;
* false otherwise. * @throws ParameterException If the set of place names is null * or some place names are null. * @see #addPlace(String) */ public final boolean addPlaces(Set placeNames) { return addPlaces(placeNames, true); } /** * Adds places with the given names to the Petri net.
* Place names have to be unique. In case the net already contains places * with given names, less places than the given number of arguments may be * added to the Petri net.
* This method calls {@link #addPlace(String)} for each place name. * * @param placeNames Names for the Petri net places to add. * @param notifyListeners Set true if listeners should be * notified. * @return true if at least one place was successfully * added;
* false otherwise. * @throws ParameterException If the set of place names is null * or some place names are null. * @see #addPlace(String) */ public final boolean addPlaces(Set placeNames, boolean notifyListeners) { Validate.notNull(placeNames); boolean updated = false; for (String placeName : placeNames) { if (addPlace(placeName, notifyListeners)) { updated = true; } } return updated; } /** * Adds a place with the given name to the Petri net.
* Place names have to be unique. In case the net already contains a place * with the given name, no place is added to the net. * * @param placeName The name for the Petri net place. * @return true if the place was successfully added to the * net;
* false otherwise. */ public boolean addPlace(String placeName) { return addPlace(placeName, true); } /** * Adds a place with the given name to the Petri net.
* Place names have to be unique. In case the net already contains a place * with the given name, no place is added to the net. * * @param placeName The name for the Petri net place. * @param notifyListeners Set true if listeners should be * notified. * @return true if the place was successfully added to the * net;
* false otherwise. */ public boolean addPlace(String placeName, boolean notifyListeners) { return addPlace(placeName, placeName, notifyListeners); } /** * Adds a place with the given name to the Petri net.
* Place names have to be unique. In case the net already contains a place * with the given name, no place is added to the net. Place labels do not * have to be unique. * * @param placeName The name for the Petri net place. * @param placeLabel The label for the Petri net place. * @return true if the place was successfully added to the * net;
* false otherwise. */ public boolean addPlace(String placeName, String placeLabel) { return addPlace(placeName, placeLabel, true); } /** * Adds a place with the given name to the Petri net.
* Place names have to be unique. In case the net already contains a place * with the given name, no place is added to the net. Place labels do not * have to be unique. * * @param placeName The name for the Petri net place. * @param placeLabel The label for the Petri net place. * @param notifyListeners Set true if listeners should be * notified. * @return true if the place was successfully added to the * net;
* false otherwise. */ public boolean addPlace(String placeName, String placeLabel, boolean notifyListeners) { if (containsPlace(placeName)) { return false; } return addPlace(createNewPlace(placeName, placeLabel), notifyListeners); } /** * Protected method for adding a place of type P to the place * map.
* This method is called from {@link #addPlace(String)} which checks if * there already exists a place with equal name before making the call. * * @param place The Petri net place to add. * @param notifyListeners Set true if listeners should be * notified. * @return true if place has been added successfully. * @throws ParameterException If the given place is null. */ protected boolean addPlace(P place, boolean notifyListeners) { Validate.notNull(place); if (containsPlace(place)) { return false; } places.put(place.getName(), place); place.addTokenListener(this); place.addPlaceListener(this); if (notifyListeners) { structureListenerSupport.notifyPlaceAdded(place); } return true; } /** * Checks if the Petri net contains the given place. * * @param place The place to check. * @return true if the net contains the transition;
* false otherwise. */ protected boolean containsPlace(P place) { return containsPlace(place.getName()); } /** * Checks if the Petri net contains a place with the given name. * * @param placeName The name of the place to check. * @return true if the net contains a place with the given * name;
* false otherwise. */ public boolean containsPlace(String placeName) { return places.keySet().contains(placeName); } /** * Creates a new place of type P with the given name.
* This method is abstract because only subclasses know the type * P of their places. * * @param name The name for the new place. * @param label The label for the new place. * @return A new place of type P with the given name. */ protected abstract P createNewPlace(String name, String label); /** * Removes the place with the given name from the Petri net.
* On removing a place, all corresponding relations are removed as well. * * @param placeName The name of the place to remove * @return true if the place was successfully removed from the * net;
* false otherwise. */ public boolean removePlace(String placeName) { return removePlace(placeName, true); } /** * Removes the place with the given name from the Petri net.
* On removing a place, all corresponding relations are removed as well. * * @param placeName The name of the place to remove * @param notifyListeners Set true if listeners should be * notified. * @return true if the place was successfully removed from the * net;
* false otherwise. */ public boolean removePlace(String placeName, boolean notifyListeners) { if (!containsPlace(placeName)) { return false; } return removePlace(places.get(placeName), notifyListeners); } protected boolean removePlace(P place, boolean notifyListeners) { if (!containsPlace(place)) { return false; } int affectedRelations = place.degree(); for (F relation : place.getIncomingRelations()) { removeFlowRelation(relation, notifyListeners); } for (F relation : place.getOutgoingRelations()) { removeFlowRelation(relation, notifyListeners); } place.removePlaceListener(this); place.removeTokenListener(this); places.remove(place.getName()); sourcePlaces.remove(place.getName()); drainPlaces.remove(place.getName()); initialMarking.remove(place.getName()); marking.remove(place.getName()); if (notifyListeners) { structureListenerSupport.notifyPlaceRemoved(place, affectedRelations); } return true; } //------- Flow Relations ------------------------------------------------------------------------ public F getFlowRelation(String name) { return relations.get(name); } /** * Returns the flow relations of the Petri net. * * @return A collection of flow relations. */ public Collection getFlowRelations() { return Collections.unmodifiableCollection(relations.values()); } public int getFlowRelationCount() { return relations.size(); } /** * Adds a flow relation starting at a place and ending at a transition. * * @param placeName The name of the place where toe relation starts. * @param transitionName The name of the transition where the relation ends. * @param constraint The constraint for the flow relation. * @return true if the flow relation was successfully * added;
* false otherwise. */ public F addFlowRelationPT(String placeName, String transitionName, S constraint) { return addFlowRelationPT(placeName, transitionName, constraint, true); } /** * Adds a flow relation starting at a place and ending at a transition. * * @param placeName The name of the place where toe relation starts. * @param transitionName The name of the transition where the relation ends. * @param constraint The constraint for the flow relation. * @param notifyListeners Set true if listeners should be * notified. * @return true if the flow relation was successfully * added;
* false otherwise. */ public F addFlowRelationPT(String placeName, String transitionName, S constraint, boolean notifyListeners) { validatePlace(placeName); validateTransition(transitionName); if (containsRelationPT(placeName, transitionName)) { return null; } F newFlowRelation = createNewFlowRelation(places.get(placeName), transitions.get(transitionName), constraint); if (addFlowRelation(newFlowRelation, notifyListeners)) { return newFlowRelation; } return null; } /** * Adds a flow relation starting at a place and ending at a transition. * * @param placeName The name of the place where toe relation starts. * @param transitionName The name of the transition where the relation ends. * @return true if the flow relation was successfully * added;
* false otherwise. */ public F addFlowRelationPT(String placeName, String transitionName) { return addFlowRelationPT(placeName, transitionName, true); } /** * Adds a flow relation starting at a place and ending at a transition. * * @param placeName The name of the place where toe relation starts. * @param transitionName The name of the transition where the relation ends. * @param notifyListeners Set true if listeners should be * notified. * @return true if the flow relation was successfully * added;
* false otherwise. */ public F addFlowRelationPT(String placeName, String transitionName, boolean notifyListeners) { validatePlace(placeName); validateTransition(transitionName); if (containsRelationPT(placeName, transitionName)) { return null; } F newFlowRelation = createNewFlowRelation(places.get(placeName), transitions.get(transitionName)); if (addFlowRelation(newFlowRelation, notifyListeners)) { return newFlowRelation; } return null; } /** * Adds a flow relation starting at a transition and ending at a place. * * @param transitionName The name of the transition where the relation ends. * @param placeName The name of the place where toe relation starts. * @param constraint Constraint of the flow relation. * @return true if the flow relation was successfully * added;
* false otherwise. */ public F addFlowRelationTP(String transitionName, String placeName, S constraint) { return addFlowRelationTP(transitionName, placeName, constraint, true); } /** * Adds a flow relation starting at a transition and ending at a place. * * @param transitionName The name of the transition where the relation ends. * @param placeName The name of the place where toe relation starts. * @param constraint Constraint of the flow relation. * @param notifyListeners Set true if listeners should be * notified. * @return true if the flow relation was successfully * added;
* false otherwise. */ public F addFlowRelationTP(String transitionName, String placeName, S constraint, boolean notifyListeners) { validatePlace(placeName); validateTransition(transitionName); if (containsRelationTP(transitionName, placeName)) { return null; } F newFlowRelation = createNewFlowRelation(transitions.get(transitionName), places.get(placeName), constraint); if (addFlowRelation(newFlowRelation, notifyListeners)) { return newFlowRelation; } return null; } /** * Adds a flow relation starting at a transition and ending at a place. * * @param transitionName The name of the transition where the relation ends. * @param placeName The name of the place where toe relation starts. * @return true if the flow relation was successfully * added;
* false otherwise. */ public F addFlowRelationTP(String transitionName, String placeName) { return addFlowRelationTP(transitionName, placeName, true); } /** * Adds a flow relation starting at a transition and ending at a place. * * @param transitionName The name of the transition where the relation ends. * @param placeName The name of the place where toe relation starts. * @param notifyListeners Set true if listeners should be * notified. * @return true if the flow relation was successfully * added;
* false otherwise. */ public F addFlowRelationTP(String transitionName, String placeName, boolean notifyListeners) { validatePlace(placeName); validateTransition(transitionName); if (containsRelationTP(transitionName, placeName)) { return null; } F newFlowRelation = createNewFlowRelation(transitions.get(transitionName), places.get(placeName)); if (addFlowRelation(newFlowRelation, notifyListeners)) { return newFlowRelation; } return null; } /** * Protected method for adding a relation of type F to the * relation map.
* This method is called from {@link #addFlowRelationPT(String, String)} and * {@link #addFlowRelationTP(String, String)} which both check if there * already exists a relation with equal name before making the call.
* They also ensure that the place/transition of the relation are * places/transitions of the net. * * @param relation The Petri net relation to add. * @param notifyListeners Set true if listeners should be * notified. * @return true if flow relation has been added successfully. * @see #checkSD(AbstractPlace) * @see #checkSD(AbstractTransition) */ protected boolean addFlowRelation(F relation, boolean notifyListeners) { Validate.notNull(relation); if (containsFlowRelation(relation)) { return false; } if (relation.getDirectionPT()) { relation.getPlace().addOutgoingRelation(relation); relation.getTransition().addIncomingRelation(relation); } else { relation.getTransition().addOutgoingRelation(relation); relation.getPlace().addIncomingRelation(relation); } relation.getTransition().checkState(); relations.put(relation.getName(), relation); checkSD(relation.getPlace()); checkSD(relation.getTransition()); relation.addRelationListener(this); if (notifyListeners) { structureListenerSupport.notifyRelationAdded(relation); } return true; } public boolean containsFlowRelation(String relationName) { return relations.keySet().contains(relationName); } protected boolean containsFlowRelation(F relation) { for (F existingRelation : getFlowRelations()) { if (existingRelation.equals(relation)) { return true; } } return false; } public boolean containsRelationPT(String placeName, String transitionName) { validatePlace(placeName); validateTransition(transitionName); for (F outgoingRelation : getPlace(placeName).getOutgoingRelations()) { if (outgoingRelation.getTransition().getName().equals(transitionName)) { return true; } } return false; } public boolean containsRelationTP(String transitionName, String placeName) { validatePlace(placeName); validateTransition(transitionName); for (F outgoingRelation : getTransition(transitionName).getOutgoingRelations()) { if (outgoingRelation.getPlace().getName().equals(placeName)) { return true; } } return false; } /** * Checks the source and drain property of the given place
* and adds/removes it from the list of source/drain places. * * @param place The Petri net place to check. */ private void checkSD(P place) { if (place.isSource()) { sourcePlaces.put(place.getName(), place); } else { sourcePlaces.remove(place.getName()); } if (place.isDrain()) { drainPlaces.put(place.getName(), place); } else { drainPlaces.remove(place.getName()); } } /** * Checks the source and drain property of the given transition
* and adds/removes it from the list of source/drain transitions. * * @param transition The Petri net transition to check. */ private void checkSD(T transition) { if (transition.isSource()) { sourceTransitions.put(transition.getName(), transition); } else { sourceTransitions.remove(transition.getName()); } if (transition.isDrain()) { drainTransitions.put(transition.getName(), transition); } else { drainTransitions.remove(transition.getName()); } } /** * Checks if the Petri net contains the given relation. * * @param relation The relation to check. * @return true if the net contains the relation;
* false otherwise. */ protected boolean containsRelation(F relation) { return relations.values().contains(relation); } /** * Creates a new relation of type F from the given place to the * given transition. This method is abstract because only subclasses know * the type F of their relations. * * @param place The place where the relation starts. * @param transition The transition where the relation ends. * @param constraint Constraint of the new flow relation. * @return A new relation of type F. */ protected abstract F createNewFlowRelation(P place, T transition, S constraint); /** * Creates a new relation of type F from the given transition * to the given place. This method is abstract because only subclasses know * the type F of their relations. * * @param transition The transition where the relation starts * @param place The place where the relation ends. * @param constraint Constraint of the new flow relation. * @return A new relation of type F. */ protected abstract F createNewFlowRelation(T transition, P place, S constraint); /** * Creates a new relation of type F from the given place to the * given transition. This method is abstract because only subclasses know * the type F of their relations. * * @param place The place where the relation starts. * @param transition The transition where the relation ends. * @return A new relation of type F. */ protected abstract F createNewFlowRelation(P place, T transition); /** * Creates a new relation of type F from the given transition * to the given place. This method is abstract because only subclasses know * the type F of their relations. * * @param transition The transition where the relation starts * @param place The place where the relation ends. * @return A new relation of type F. */ protected abstract F createNewFlowRelation(T transition, P place); /** * Removes the given relation from the net.
* This method is used by {@link #removePlace(String)} and should not be * accessed in a public way.
* The caller is responsible for parameter validity. * * @param relation The relation to remove. * @param notifyListeners Set true if listeners should be * notified * @return true if flow relation has been removed successfully, * false otherwise */ protected boolean removeFlowRelation(F relation, boolean notifyListeners) { if (!containsRelation(relation)) { return false; } relation.getPlace().removeRelation(relation); relation.getTransition().removeRelation(relation); relations.remove(relation.getName()); relation.removeRelationListener(this); checkSD(relation.getPlace()); checkSD(relation.getTransition()); if (notifyListeners) { structureListenerSupport.notifyRelationRemoved(relation); } return true; } public boolean removeFlowRelation(String relationName) { return removeFlowRelation(relationName, true); } public boolean removeFlowRelation(String relationName, boolean notifyListeners) { if (!containsFlowRelation(relationName)) { return false; } return removeFlowRelation(relations.get(relationName), notifyListeners); } //------- Markings ------------------------------------------------------------------------------- /** * Warning: This non-final method gets called by constructors. * * @return Returns the created marking */ public abstract M createNewMarking(); /** * Sets the initial marking of the Petri net.
* The initial marking defines the initial state of a Petri net in the sense * of tokens contained in places. When a Petri net is reset * ({@link #reset()}), it should reach its initial marking.
* The initial marking does not have to contain all net places. For places * not contained in the given initial marking, the net assumes 0 tokens in * the initial net state.
* This method uses {@link #setInitialMarking(AbstractMarking)} to actually * transform the net state to the initial state. * * @param marking The marking used as initial marking (state) of the net. */ @SuppressWarnings("unchecked") public void setInitialMarking(M marking) { validateMarking(marking); // if(initialMarking != null) // initialMarking.clear(); initialMarking = createNewMarking(); for (String placeName : marking.places()) { initialMarking.set(placeName, marking.get(placeName)); } markingListenerSupport.notifyInitialMarkingChanged(new MarkingChangeEvent<>(initialMarking)); setMarking((M) initialMarking.clone()); } /** * Returns the initial marking of the Petri net.
* The initial marking defines the initial state of a Petri net in the sense * of tokens contained in places. * * @return The initial marking of the net. */ @SuppressWarnings("unchecked") public M getInitialMarking() { return (M) initialMarking.clone(); } /** * Sets the marking of the Petri net.
* The net marking defines states for Petri net classes.
* In case the given marking does not define a state for a net place,
* its state is set to empty, i.e. all tokens are removed. * * @param marking The desired net marking. */ public void setMarking(M marking) { validateMarking(marking); // if(this.marking != null) // this.marking.clear(); this.marking = createNewMarking(); for (String placeName : places.keySet()) { if (marking.contains(placeName)) { this.marking.set(placeName, marking.get(placeName)); getPlace(placeName).setState(marking.get(placeName)); } else { getPlace(placeName).setEmptyState(); } } lastFiredTransition = null; markingListenerSupport.notifyMarkingChanged(new MarkingChangeEvent<>(marking)); } /** * Returns the actual marking (state) of the Petri net which is defined by * the number of tokens contained in net places. * * @return The actual marking (state) of the net. */ @SuppressWarnings("unchecked") public M getMarking() { return (M) marking.clone(); } /** * Updates the marking (state) of the Petri net for the given place.
* When the number of tokens in places change, the marking of the net which * describes its actual state in the sense of tokens in net places has to be * updated. * * @param place Place whose token number has changed. */ protected void updateMarking(P place) { Validate.notNull(place); if (!places.containsKey(place.getName())) { return; } if (!place.hasEmptyState()) { marking.set(place.getName(), place.getState()); } else { marking.remove(place.getName()); } markingListenerSupport.notifyMarkingChanged(new MarkingChangeEvent<>(marking)); } //------- Functionality ------------------------------------------------------------------------ /** * Fires the net transition with the given name. * * @param transitionName The name of the transition to fire. * @return true if the transition could be fired;
* false otherwise. * @throws ParameterException If the given transition name is * null
* or the net does not contain a transition with the given name. * @throws PNException If the net transition with the given name is not * enabled. */ public T fire(String transitionName) throws PNException { validateFireTransition(transitionName); T transition = getTransition(transitionName); transition.fire(); lastFiredTransition = transition; return transition; } /** * Returns the marking which is the result of firing the transition with the * given name.
* The transition is not actually fired, but its firing impact with respect * to the actual net state
* simulated and returned in form of a resulting marking. * * @param transitionName The name of the transition to fire. * @return true if the transition could be fired;
* false otherwise. * @throws ParameterException If the given transition name is * null
* or the net does not contain a transition with the given name. * @throws PNException If the net transition with the given name is not * enabled. */ public abstract M fireCheck(String transitionName) throws PNException; /** * Returns a copy of the actual net state (marking). * * @return A copy of the nets' actual marking. */ @SuppressWarnings("unchecked") public M cloneMarking() { return (M) getMarking().clone(); } /** * Resets the net to the initial state.
* This includes setting the actual marking to the actual marking and * setting the last fired transition to null. */ @SuppressWarnings("unchecked") public void reset() { setMarking((M) initialMarking.clone()); } //------- Validation methods -------------------------------------------------------------------- @Override public void capacityChanged(CapacityEvent> o) { } /** * This method can be used to validate transition names.
* It checks if it is null and if the Petri net contains a * transition with this name. * * @param transitionName The name of the transition in question. */ protected void validateTransition(String transitionName) { Validate.notNull(transitionName); if (!containsTransition(transitionName)) { throw new ParameterException(ErrorCode.INCOMPATIBILITY, "Net does not contain a transition with name \"" + transitionName + "\""); } } /** * This method can be used to validate place names.
* It checks if it is null and if the Petri net contains a * place with this name. * * @param placeName The name of the place in question. */ protected void validatePlace(String placeName) { Validate.notNull(placeName); if (!containsPlace(placeName)) { throw new ParameterException(ErrorCode.INCOMPATIBILITY, "Net does not contain a place with name \"" + placeName + "\""); } } /** * This method can be used to validate the name of a transition to fire.
* It checks if the given name is null, if the Petri net * contains a transition with this name
* and if this transition is enabled. * * @param transitionName The name of the transition in question. * @throws PNException If the net transition with the given name is not * enabled. */ protected void validateFireTransition(String transitionName) throws PNException { validateTransition(transitionName); T transition = getTransition(transitionName); if (!enabledTransitions.contains(transition)) { throw new PNException("Cannot fire disabled transition " + transitionName); } } /** * This method can be used to evaluate the compatibility markings to be * assigned to the Petri net.
* It checks if the Petri net contains all places for which the marking * holds states. * * @param marking The marking in question. */ protected void validateMarking(M marking) { Validate.notNull(marking); if (!places.keySet().containsAll(marking.places())) { throw new ParameterException(ErrorCode.INCOMPATIBILITY, "Places in initial marking must be places of the net."); } } //------- Interface Methods ---------------------------------------------------------------------- @Override public Set> getParents(AbstractPNNode node) { return node.getParents(); } @Override public Set> getChildren(AbstractPNNode node) { return node.getChildren(); } @Override public int nodeCount() { return transitions.size() + places.size(); } @Override public Set> getNodes() { Set> result = new HashSet<>(); result.addAll(getTransitions()); result.addAll(getPlaces()); return result; } //------ Listener methods ------------------------------------------------------------------------ public boolean addStructureListener(PNStructureListener listener) { return structureListenerSupport.addListener(listener); } public boolean removeStructureListener(PNStructureListener listener) { return structureListenerSupport.removeListener(listener); } public boolean addMarkingListener(PNMarkingListener listener) { return markingListenerSupport.addListener(listener); } public boolean removeMarkingListener(PNMarkingListener listener) { return markingListenerSupport.removeListener(listener); } /** * When a transition changes its state to enabled,
* the net adds it to the set of enabled transitions. * * @param e */ @SuppressWarnings("unchecked") @Override public void transitionEnabled(TransitionEvent> e) { enabledTransitions.add((T) e.getSource()); } /** * When a transition changes its state to disabled,
* the net removes it from the set of enabled transitions. * * @param e */ @Override public void transitionDisabled(TransitionEvent> e) { enabledTransitions.remove(e.getSource()); } /** * When tokens are added to net places,
* the Petri net updates its state accordingly by adding tokens to its * marking. * * @param o * @see #updateMarking(AbstractPlace) */ @SuppressWarnings("unchecked") @Override public void tokensAdded(TokenEvent> o) { updateMarking((P) o.getSource()); } /** * When tokens are removed from net places,
* the Petri net updates its state accordingly by removing tokens from its * marking. * * @param o * @see #updateMarking(AbstractPlace) */ @SuppressWarnings("unchecked") @Override public void tokensRemoved(TokenEvent> o) { updateMarking((P) o.getSource()); } @Override public void markingChanged(MarkingChangeEvent markingEvent) { } @Override public void initialMarkingChanged(MarkingChangeEvent markingEvent) { } @Override public void relationConstraintChanged(RelationConstraintEvent> e) { structureListenerSupport.notifyStructureChange(); } @Override public void structureChanged() { } @Override public void placeAdded(PlaceChangeEvent

event) { } @Override public void placeRemoved(PlaceChangeEvent

event) { } @Override public void transitionAdded(TransitionChangeEvent event) { } @Override public void transitionRemoved(TransitionChangeEvent event) { } @Override public void relationAdded(RelationChangeEvent event) { } @Override public void relationRemoved(RelationChangeEvent event) { } //------- clone ---------------------------------------------------------------------------------- @Override public boolean nameChangeRequest(AbstractPlace place, String newName) { if (!places.containsValue(place)) { // This Petri net does not contain the place whose name is changed. // -> Don't care and return true return true; } if (containsPlace(newName) && getPlace(newName) != place) { // The Petri net already contains another place with the same name // -> Don't allow the name to be changed! return false; } if (containsTransition(newName)) { // The Petri net contains a transition with the same name // -> Don't allow the name to be changed! return false; } // The Petri net contains the given place and no other node with the desired name. // -> Allow the name change and store the place under the new name P memberPlace = places.remove(place.getName()); places.put(newName, memberPlace); return true; } @Override public boolean nameChangeRequest(AbstractTransition transition, String newName) { if (!transitions.containsValue(transition)) { // This Petri net does not contain the transition whose name is changed. // -> Don't care and return true return true; } if (containsTransition(newName) && getTransition(newName) != transition) { // The Petri net already contains another transition with the same name // -> Don't allow the name to be changed! return false; } if (containsPlace(newName)) { // The Petri net contains a place with the same name // -> Don't allow the name to be changed! return false; } // The Petri net contains the given transition and no other node with the desired name. // -> Allow the name change and store the transition under the new name T memberTransition = transitions.remove(transition.getName()); transitions.put(newName, memberTransition); return true; } @Override public boolean nameChangeRequest(AbstractFlowRelation relation, String newName) { if (!relations.containsValue(relation)) { // This Petri net does not contain the flow relation whose name is changed. // -> Don't care and return true return true; } if (containsFlowRelation(newName) && getFlowRelation(newName) != relation) { // The Petri net already contains another flow relation with the same name // -> Don't allow the name to be changed! return false; } // The Petri net contains the given flow relation and no other flow relation with the desired name. // -> Allow the name change and store the relation under the new name F memberRelation = relations.remove(relation.getName()); relations.put(newName, memberRelation); return true; } @SuppressWarnings("unchecked") @Override public AbstractPetriNet clone() { AbstractPetriNet result = newInstance(); Map clonedTransitions = new HashMap<>(); Map clonedPlaces = new HashMap<>(); for (T ownTransition : getTransitions()) { clonedTransitions.put(ownTransition, (T) ownTransition.clone()); result.addTransition(clonedTransitions.get(ownTransition), false); } for (P ownPlace : getPlaces()) { clonedPlaces.put(ownPlace, (P) ownPlace.clone()); result.addPlace(clonedPlaces.get(ownPlace), false); } for (F ownRelation : getFlowRelations()) { result.addFlowRelation((F) ownRelation.clone(clonedPlaces.get(ownRelation.getPlace()), clonedTransitions.get(ownRelation.getTransition()), ownRelation.getDirectionPT()), false); } result.setInitialMarking((M) getInitialMarking().clone()); return result; } public abstract AbstractPetriNet newInstance(); // @SuppressWarnings("unchecked") // public AbstractMarkingGraph buildMarkingGraph() throws PNException{ // // if (!isBounded()) // throw new ParameterException(ErrorCode.INCOMPATIBILITY, "Cannot determine marking graph for unbounded nets."); // // ArrayBlockingQueue queue = new ArrayBlockingQueue(10); // Set allKnownStates = new HashSet(); // // allKnownStates.add(getInitialMarking()); // AbstractMarkingGraph markingGraph = createNewMarkingGraph(); // int stateCount = 0; // Map stateNames = new HashMap(); // M initialMarking = getInitialMarking(); // queue.offer(initialMarking); // String stateName = String.format(rgGraphNodeFormat, stateCount++); // markingGraph.addState(stateName, (M) initialMarking.clone()); // markingGraph.setInitialState(markingGraph.getState(stateName)); // markingGraph.addStartState(stateName); // stateNames.put(initialMarking.hashCode(), stateName); // allKnownStates.add((M) initialMarking.clone()); // // while (!queue.isEmpty()) { // M nextMarking = queue.poll(); // // System.out.println("Next marking: " + nextMarking); // setMarking(nextMarking); // // M marking = (M) nextMarking.clone(); // String nextStateName = stateNames.get(nextMarking.hashCode()); // // System.out.println(nextStateName + " " + nextMarking); // // if (hasEnabledTransitions()) { // String newStateName = null; // for (T enabledTransition : getEnabledTransitions()) { // // // System.out.println("enabled: " + enabledTransition.getName()); // M newMarking = fireCheck(enabledTransition.getName()); // int newMarkingHash = newMarking.hashCode(); // // System.out.println("new marking: " + newMarking); // // // Check if this marking is already known // M equalMarking = null; // for (M storedMarking : allKnownStates) { // if (storedMarking.equals(newMarking)) { // equalMarking = storedMarking; // break; // } // } // // // System.out.println("new marking: " + newMarking); // if (equalMarking == null) { // // This is a new marking // // System.out.println("New marking"); // queue.offer(newMarking); // allKnownStates.add((M) newMarking.clone()); // newStateName = String.format(rgGraphNodeFormat, stateCount++); // markingGraph.addState(newStateName, (M) newMarking.clone()); // stateNames.put(newMarkingHash, newStateName); // } else { // // This marking is already known // // System.out.println("Known marking"); // newStateName = stateNames.get(newMarkingHash); // } // if (!markingGraph.containsEvent(enabledTransition.getName())) { // markingGraph.addEvent(enabledTransition.getName(), enabledTransition.getLabel()); // } // // System.out.println("add relation: " + nextStateName + " to " + newStateName + " via " + enabledTransition.getName()); // try { // markingGraph.addRelation(nextStateName, newStateName, enabledTransition.getName()); // } catch (Exception e) { // throw new PNException("TS-Exception while building marking graph.
Reason: " + e.getMessage()); // } // } // } else { // markingGraph.addEndState(nextStateName); // } // // } // this.markingGraph = markingGraph; // reset(); // return markingGraph; // } // public PNComplexity getComplexity() { return new PNComplexity(getPlaceCount(), getTransitionCount(), getFlowRelationCount()); } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((initialMarking == null) ? 0 : initialMarking.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + ((places == null) ? 0 : places.hashCode()); result = prime * result + ((relations == null) ? 0 : relations.hashCode()); result = prime * result + ((transitions == null) ? 0 : transitions.hashCode()); return result; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } @SuppressWarnings("rawtypes") AbstractPetriNet other = (AbstractPetriNet) obj; if (initialMarking == null) { if (other.initialMarking != null) { return false; } } else if (!initialMarking.equals(other.initialMarking)) { return false; } if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } if (places == null) { if (other.places != null) { return false; } } else if (!places.equals(other.places)) { return false; } if (relations == null) { if (other.relations != null) { return false; } } else if (!relations.equals(other.relations)) { return false; } if (transitions == null) { if (other.transitions != null) { return false; } } else if (!transitions.equals(other.transitions)) { return false; } return true; } public enum Boundedness { BOUNDED, UNBOUNDED, UNKNOWN } }