de.uni.freiburg.iig.telematik.sepia.petrinet.abstr.AbstractPlace Maven / Gradle / Ivy
package de.uni.freiburg.iig.telematik.sepia.petrinet.abstr;
import java.io.Serializable;
import java.util.List;
import de.invation.code.toval.validate.ParameterException;
import de.invation.code.toval.validate.Validate;
import de.uni.freiburg.iig.telematik.sepia.event.CapacityEvent;
import de.uni.freiburg.iig.telematik.sepia.event.PlaceListener;
import de.uni.freiburg.iig.telematik.sepia.event.PlaceListenerSupport;
import de.uni.freiburg.iig.telematik.sepia.event.TokenListener;
import de.uni.freiburg.iig.telematik.sepia.event.TokenListenerSupport;
import de.uni.freiburg.iig.telematik.sepia.exception.PNValidationException;
/**
* Abstract class that defines general properties for Petri net places.
* This class inherits basic Petri net node properties from {@link AbstractPNNode}
* and defines additional properties:
*
* - Capacity: The maximum number of tokens that can reside in the place.
* A capacity of -1 is interpreted as unboundedness, i.e. the number and kind of tokens is not restricted.
*
*
* A place maintains a state of type S, which defines the number and kind of tokens in the place.
* The type S is used for the internal state of the place and for adding and removing tokens from the place. A place allows {@link TokenListener}s to register so that they can be notified on state changes,
* i.e. added/removed tokens.
*
* @author Thomas Stocker
*
* @param
* The type of flow relations connected to the place.
* @param
* Type for token number and type.
*/
public abstract class AbstractPlace,
? extends AbstractTransition, S>,
S extends Object>
extends AbstractPNNode implements Serializable{
private static final long serialVersionUID = -4942014250666222432L;
/**
* Support class for {@link TokenListener} handling.
*/
protected TokenListenerSupport> tokenListenerSupport = new TokenListenerSupport>();
/**
* Support class for {@link PlaceListener} handling.
*/
protected PlaceListenerSupport> placeListenerSupport = new PlaceListenerSupport>();
/**
* Capacity of the place, i.e. the maximum number of tokens the place can hold.
* A capacity of -1 is interpreted as unboundedness, i.e. the number and kind of tokens is not restricted.
*/
protected int capacity = -1;
/**
* The state of the place, i.e. the number and kind of tokens that reside in the place.
*/
protected S state = null;
// ------- Constructors --------------------------------------------------------------------------
// protected AbstractPlace() {
// super(PNNodeType.PLACE);
// }
/**
* Creates a new place with the given name.
* @param name The name for the new Place.
* @throws ParameterException If the given name is null.
*/
public AbstractPlace(String name){
super(PNNodeType.PLACE, name);
}
/**
* Creates a new place with the given name and label.
*
* @param name
* The name for the new place.
* @param label
* The label for the new place.
*/
public AbstractPlace(String name, String label){
super(PNNodeType.PLACE, name, label);
}
// ------- Basic properties -----------------------------------------------------------------------
/**
* Returns the capacity of the place, i.e.
* the maximum number of tokens the place can hold.
*
* @return The capacity of the place.
*/
public int getCapacity() {
return capacity;
}
/**
* Sets the capacity of the place, i.e.
* the maximum number of tokens the place can hold.
* The capacity must be > 0.
*
* @param capacity
* The desired capacity.
*/
public void setCapacity(int capacity){
Validate.bigger(capacity, 0);
int oldCapacity = this.capacity;
this.capacity = capacity;
if (oldCapacity != capacity)
placeListenerSupport.notifyCapacityChanged(new CapacityEvent>(this, capacity));
}
/**
* Removes the capacity restriction of the place, i.e.
* the maximum number of tokens the place can hold.
*/
public void removeCapacity() {
capacity = -1;
}
/**
* Checks, if the place is bounded.
* Bounded places restrict the number and kind of tokens that can reside in the place.
* A capacity of -1 is interpreted as unboundedness.
*
* @return true if the place is bounded,
* false otherwise.
*/
public boolean isBounded() {
return getCapacity() > -1;
}
/**
* checks if this place and the given place have the same relations.
*
* @param otherPlace
* The place in question.
* @return true if the two places have the same relations;
* false otherwise.
*/
public boolean hasEqualRelations(AbstractPlace otherPlace){
Validate.notNull(otherPlace);
List incomingRelations = otherPlace.getIncomingRelations();
List outgoingRelations = otherPlace.getOutgoingRelations();
if ((this.incomingRelations.size() != incomingRelations.size()) || (this.outgoingRelations.size() != outgoingRelations.size())) {
return false;
}
for (AbstractFlowRelation extends AbstractPlace, ? extends AbstractTransition, S> incoming : incomingRelations) {
if (!this.containsRelationFrom(incoming.getTransition()))
return false;
}
for (AbstractFlowRelation extends AbstractPlace, ? extends AbstractTransition, S> outgoing : outgoingRelations) {
if (!this.containsRelationTo(outgoing.getTransition()))
return false;
}
return true;
}
// @Override
// protected void setName(String name){
// Validate.notNull(name);
// if(!placeListenerSupport.requestNameChangePermission(this, name))
// throw new ParameterException(ErrorCode.INCONSISTENCY, "A connected Petri net already contains a node with this name.\n Cancel renaming to avoid name clash.");
// this.name = name;
// }
public int outDegree(){
return getOutgoingRelations().size();
}
public int inDegree(){
return getIncomingRelations().size();
}
// ------- State manipulation -----------------------------------------------------------------------------
/**
* Returns the state of the place in terms of number and kind of tokens.
* The returned state should NOT be used to manually add or remove tokens from a place.
* Token adding/removal is done by Petri net transitions.
*
* @return The actual state of the place.
* @see AbstractPlace#state
*/
public abstract S getState();
/**
* Sets the state of the place in terms of number and kind of tokens if the place is not already in this state.
* Before the state is set, its validity is checked with the help of {@link #validateState(Object)}.
* On state changes, the place tells connected transitions to update their state
* and calls the method {@link #stateChange(Object, Object)} which can be
* overridden in subclasses to take further actions on state changes.
*
* @param state
* The desired state of the place.
* @return true if the actual place state was changed to the given state;
* false if the place is already in the given state.
* @see #initiateStateChecks()
*/
public boolean setState(S state){
validateState(state);
if (this.state.equals(state))
return false;
S oldState = this.state;
this.state = state;
// Tell outgoing transitions to update their state.
initiateStateChecks();
// Call state-change method
stateChange(oldState, state);
return true;
}
/**
* Adds all given tokens to the place.
* The number and type of tokens to add are defined by type S.
*
* @param tokens
* The number and type of tokens to add.
* @throws ParameterException
* If the given parameter is null or invalid,
* or if the tokens cannot be added to the place, e.g. due to capacity restrictions.
*/
protected abstract void addTokens(S tokens);
/**
* Removes all given tokens from the place.
* The number and type of tokens to remove are defined by type S.
*
* @param tokens
* The number and type of tokens to remove.
* @throws ParameterException
* If the given parameter is null or invalid,
* or if the tokens cannot be removed from the place.
*/
protected abstract void removeTokens(S tokens);
/**
* Checks, if the place can consume the given state, i.e.
* merging the actual state and the given state does not exceed the places' capacity.
* This method does NOT actually "add" the given state to the places' actual state.
*
* @param state
* The state to merge.
* @return true if the place can consume the state;
* false otherwise.
* @throws ParameterException
* if the given state is null.
*/
public abstract boolean canConsume(S state);
/**
* Checks if the place state is empty, i.e. the place contains no tokens.
* Depending on the state representation this may require different check routines,
* which have to be implemented by subclasses.
*
* @return true if the place contains not tokens;
* false otherwise.
*/
public abstract boolean hasEmptyState();
/**
* Sets the state of the place in empty condition, i.e. removes all tokens from the place.
* Depending on the state representation this may require different check routines,
* which have to be implemented by subclasses.
* @see #getEmptyState()
*/
public boolean setEmptyState(){
S emptyState = getEmptyState();
if(getState().equals(emptyState))
return false;
S oldState = getState();
this.state = emptyState;
// Tell outgoing transitions to update their state.
initiateStateChecks();
// Call state-change method
stateChange(oldState, emptyState);
return true;
}
/**
* Gets the state of the place in empty condition.
*/
public abstract S getEmptyState();
/**
* This method is called by {@link #setState(Object)} in case of state changes.
* It can be overridden in subclasses to take further actions on state changes.
*
* @param oldState
* Old place state before change.
* @param newState
* New place state after change.
*/
protected abstract void stateChange(S oldState, S newState);
/**
* Tells outgoing transitions to update their state.
* Depending on token changes outgoing transitions may get enabled/disabled.
*/
protected void initiateStateChecks() {
for (AbstractFlowRelation extends AbstractPlace, ? extends AbstractTransition, S> r : outgoingRelations.values()) {
r.getTransition().checkState();
}
for (AbstractFlowRelation extends AbstractPlace, ? extends AbstractTransition, S> r : incomingRelations.values()) {
r.getTransition().checkState();
}
}
// ------- Validity -------------------------------------------------------------------------------
/**
* Checks if the Petri net place is valid.
* Subclasses may define the validity of a net place e.g. in terms of specific constraints and must throw PNValidationExceptions in case any constraint is violated.
*
* @throws PNValidationException
*/
public void checkValidity() throws PNValidationException {}
// ------- Validation methods ----------------------------------------------------------------------
/**
* Checks if the given state is valid.
* The exact definition of validity can be defined in subclasses by overriding this method.
* Default behavior is to check null-pointers.
*
* @param state
* The state to validate.
* @throws ParameterException
* If the given state is null.
*/
protected void validateState(S state){
Validate.notNull(state);
}
// ------- Listener support -----------------------------------------------------------------------
/**
* Adds a token listener.
*
* @param l
* The token listener to add.
* @throws ParameterException
* If the listener reference is null.
*/
public void addTokenListener(TokenListener> l){
tokenListenerSupport.addListener(l);
}
/**
* Removes a token listener.
*
* @param l
* The token listener to remove.
* @throws ParameterException
* If the listener reference is null.
*/
public void removeTokenListener(TokenListener> l){
tokenListenerSupport.removeListener(l);
}
/**
* Adds a capacity listener.
* @param l The capacity listener to add.
* @throws ParameterException If the listener reference is null.
*/
public void addPlaceListener(PlaceListener> l){
placeListenerSupport.addListener(l);
}
/**
* Removes a capacity listener.
* @param l The capacity listener to remove.
* @throws ParameterException If the listener reference is null.
*/
public void removePlaceListener(PlaceListener> l){
placeListenerSupport.removeListener(l);
}
@Override
protected abstract AbstractPlace newInstance(String name);
// ------- hashCode and equals --------------------------------------------------------------------
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + capacity;
result = prime * result + ((state == null) ? 0 : state.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
@SuppressWarnings("unchecked")
AbstractPlace other = (AbstractPlace) obj;
if (capacity != other.capacity) {
return false;
}
if (state == null) {
if (other.state != null) {
return false;
}
} else if (!state.equals(other.state)) {
return false;
}
return true;
}
// ------- clone ----------------------------------------------------------------------------------
@Override
public AbstractPlace clone() {
@SuppressWarnings("unchecked")
AbstractPlace result = (AbstractPlace) super.clone();
cloneCapacity(result);
result.setState(getState());
return result;
}
protected void cloneCapacity(AbstractPlace clone){
if (getCapacity() > 0)
clone.setCapacity(getCapacity());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy