package sushi.bpmn.element;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.jbpt.hypergraph.abs.IGObject;
import org.jbpt.hypergraph.abs.IVertex;
import sushi.bpmn.monitoringpoint.MonitoringPoint;
import sushi.bpmn.monitoringpoint.MonitoringPointStateTransition;
import sushi.persistence.Persistable;
/**
* This class is a logical representation for a BPMN element.
* @author micha
*/
@Entity
@Table(name = "BPMNElement")
@Inheritance(strategy=InheritanceType.JOINED)
public abstract class AbstractBPMNElement extends Persistable implements IVertex {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID")
protected int ID;
@Column(name = "BPMN_ID")
private String BPMN_ID;
@Column(name = "Name")
private String name;
@ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@JoinTable(name="BPMNElement_Predecessors")
private Set<AbstractBPMNElement> predecessors = new HashSet<AbstractBPMNElement>();
@ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@JoinTable(name="BPMNElement_Successors")
private Set<AbstractBPMNElement> successors = new HashSet<AbstractBPMNElement>();
@OneToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
@JoinTable(name="BPMNElement_MonitoringPoints")
private List<MonitoringPoint> monitoringPoints = new ArrayList<MonitoringPoint>();
public AbstractBPMNElement() {
this.ID = 0;
this.name = "";
}
public AbstractBPMNElement(String ID, String name) {
this.BPMN_ID = ID;
this.name = name;
}
public AbstractBPMNElement(String ID, String name, List<MonitoringPoint> monitoringPoints) {
this.BPMN_ID = ID;
this.name = name;
if(monitoringPoints != null){
this.monitoringPoints = monitoringPoints;
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<AbstractBPMNElement> getPredecessors() {
return predecessors;
}
/**
* Returns all elements, that are predecessor of the current element and its predecessor (all indirect predecessors).
* @return
*/
public Set<AbstractBPMNElement> getIndirectPredecessors(){
Set<AbstractBPMNElement> elements = new HashSet<AbstractBPMNElement>();
elements = getIndirectPredecessorsWithSelf(this, elements);
return elements;
}
private Set<AbstractBPMNElement> getIndirectPredecessorsWithSelf(AbstractBPMNElement element, Set<AbstractBPMNElement> elements){
if(element instanceof BPMNStartEvent){
if(!elements.contains(element)){
elements.add(element);
}
} else {
elements.add(element);
for(AbstractBPMNElement predecessor : element.getPredecessors()){
if(!elements.contains(predecessor)){
elements.addAll(getIndirectPredecessorsWithSelf(predecessor, elements));
}
}
}
return elements;
}
/**
* Returns all elements, that are on a path starting from the current element (all indirect successors).
* @return
*/
public Set<AbstractBPMNElement> getIndirectSuccessors(){
Set<AbstractBPMNElement> elements = new HashSet<AbstractBPMNElement>();
for(AbstractBPMNElement element : this.getSuccessors()){
elements.addAll(getIndirectSuccessors(element, elements));
}
return elements;
}
/**
* Proofs, if the given element is a indirect successor of the current element.
* @param element
* @return
*/
public boolean isIndirectSuccessor(AbstractBPMNElement element){
if(!this.equals(element)){
return this.getIndirectSuccessors().contains(element);
}
return false;
}
private Set<AbstractBPMNElement> getIndirectSuccessors(AbstractBPMNElement element, Set<AbstractBPMNElement> elements){
if(element instanceof BPMNEndEvent){
if(!elements.contains(element)){
elements.add(element);
}
} else {
elements.add(element);
for(AbstractBPMNElement successor : element.getSuccessors()){
if(!elements.contains(successor)){
elements.add(successor);
elements.addAll(getIndirectSuccessors(successor, elements));
}
}
}
return elements;
}
public void setPredecessor(Set<AbstractBPMNElement> predecessor) {
this.predecessors = predecessor;
}
/**
* Returns the direct successors.
* @return
*/
public Set<AbstractBPMNElement> getSuccessors() {
return successors;
}
public void setSuccessors(Set<AbstractBPMNElement> successors) {
this.successors = successors;
}
/**
* Adds an element as a predecessor for the current element.
* @param element
*/
public void addPredecessor(AbstractBPMNElement element) {
if(!predecessors.contains(element)){
this.predecessors.add(element);
}
}
public void removePredecessor(AbstractBPMNElement element) {
this.predecessors.remove(element);
}
public void removeAllPredecessors() {
this.predecessors = new HashSet<AbstractBPMNElement>();
}
/**
* Proofs, if this element has any predecessors.
* @return
*/
public boolean hasPredecessor() {
return (! this.predecessors.isEmpty());
}
/**
* Proofs, if this element has any successors.
* @return
*/
public boolean hasSuccessors() {
return (! this.successors.isEmpty());
}
public boolean isSequenceFlow() {
return false;
}
public boolean isBoundaryEvent() {
return false;
}
public boolean isProcess() {
return false;
}
/**
* Adds an element as a successor for the current element.
* @param element
*/
public void addSuccessor(AbstractBPMNElement element) {
if(!successors.contains(element)){
this.successors.add(element);
}
}
public void removeSuccessor(AbstractBPMNElement element) {
this.successors.remove(element);
}
public void removeAllSuccessors() {
this.successors = new HashSet<AbstractBPMNElement>();
}
public String getBPMN_ID() {
return BPMN_ID;
}
public void setBPMN_ID(String id) {
this.BPMN_ID = id;
}
public int getID() {
return ID;
}
public void setID(int id) {
this.ID = id;
}
public List<MonitoringPoint> getMonitoringPoints() {
return monitoringPoints;
}
/**
* Returns the monitoring point with the given {@link MonitoringPointStateTransition} or null.
* @param transitionType
* @return
*/
public MonitoringPoint getMonitoringPointByStateTransitionType(MonitoringPointStateTransition transitionType){
for(MonitoringPoint monitoringPoint : monitoringPoints){
if(monitoringPoint.getStateTransitionType().equals(transitionType)){
return monitoringPoint;
}
}
return null;
}
public void addMonitoringPoint(MonitoringPoint monitoringPoint){
if(getMonitoringPointByStateTransitionType(monitoringPoint.getStateTransitionType()) != null){
monitoringPoints.remove(getMonitoringPointByStateTransitionType(monitoringPoint.getStateTransitionType()));
}
monitoringPoints.add(monitoringPoint);
}
public void removeMonitoringPoint(MonitoringPoint monitoringPoint){
monitoringPoints.remove(monitoringPoint);
}
public void setMonitoringPoints(List<MonitoringPoint> monitoringPoints) {
this.monitoringPoints = monitoringPoints;
}
public boolean hasMonitoringPoints() {
return monitoringPoints != null && !monitoringPoints.isEmpty();
}
/**
* Returns true, if an element has monitoring points and if the monitoring points have an assigned event type.
* @return
*/
public boolean hasMonitoringPointsWithEventType() {
if(hasMonitoringPoints()){
boolean monitoringPointWithEventType = false;
for(MonitoringPoint monitoringPoint : monitoringPoints){
if(monitoringPoint.getEventType() != null){
monitoringPointWithEventType = true;
}
}
return monitoringPointWithEventType;
}
return false;
}
public String toString(){
return (this.name.isEmpty()) ? this.getClass().getSimpleName() : this.getClass().getSimpleName() + ": " + this.name;
}
public AbstractBPMNElement clone() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return (AbstractBPMNElement) ois.readObject();
} catch (IOException e) {
return null;
} catch (ClassNotFoundException e) {
return null;
}
}
public String getId() {
return BPMN_ID;
}
public void setId(String id) {
this.BPMN_ID = id;
}
/**
* Returns all elements on a path between the given start and end element.
* Their must exist a path between these elements.
* @param startElement - excluded
* @param endElement - excluded
* @return
*/
public static Set<AbstractBPMNElement> getElementsOnPathBetween(AbstractBPMNElement startElement, AbstractBPMNElement endElement){
Set<AbstractBPMNElement> elements = new HashSet<AbstractBPMNElement>();
if(startElement.equals(endElement)){
return elements;
}
elements.add(startElement);
for(AbstractBPMNElement successor : startElement.getSuccessors()){
if(successor.equals(endElement)){
elements.add(successor);
return elements;
}
if(successor.getIndirectSuccessors().contains(endElement) && !elements.contains(successor)){
elements.add(successor);
elements.addAll(getElementsOnPathBetweenWithStartAndEnd(successor, endElement));
}
}
elements.remove(startElement);
elements.remove(endElement);
return elements;
}
private static Set<AbstractBPMNElement> getElementsOnPathBetweenWithStartAndEnd(AbstractBPMNElement startElement, AbstractBPMNElement endElement){
Set<AbstractBPMNElement> elements = new HashSet<AbstractBPMNElement>();
elements.add(startElement);
for(AbstractBPMNElement successor : startElement.getSuccessors()){
if(successor.equals(endElement)){
elements.add(successor);
return elements;
}
if(successor.getIndirectSuccessors().contains(endElement) && !elements.contains(successor)){
elements.add(successor);
elements.addAll(getElementsOnPathBetweenWithStartAndEnd(successor, endElement));
}
}
return elements;
}
/**
* Returns all elements, that belong to the shortest path between the given start and end element.
* @param sourceElement
* @param destinationElement
* @return
*/
public static List<AbstractBPMNElement> getShortestPathBetween(AbstractBPMNElement sourceElement, AbstractBPMNElement destinationElement){
Map<AbstractBPMNElement, Boolean> visitedElements = new HashMap<AbstractBPMNElement, Boolean>();
Map<AbstractBPMNElement, AbstractBPMNElement> previousElements = new HashMap<AbstractBPMNElement, AbstractBPMNElement>();
List<AbstractBPMNElement> directions = new LinkedList<AbstractBPMNElement>();
Queue<AbstractBPMNElement> queue = new LinkedList<AbstractBPMNElement>();
AbstractBPMNElement current = sourceElement;
queue.add(current);
visitedElements.put(current, true);
while(!queue.isEmpty()){
current = queue.remove();
if (current.equals(destinationElement)){
break;
}else{
for(AbstractBPMNElement node : current.getSuccessors()){
if(!visitedElements.containsKey(node)){
queue.add(node);
visitedElements.put(node, true);
previousElements.put(node, current);
}
}
}
}
if (!current.equals(destinationElement)){
System.out.println("can't reach destination");
}
for(AbstractBPMNElement node = destinationElement; node != null; node = previousElements.get(node)) {
directions.add(node);
}
Collections.reverse(directions);
return directions;
}
@Override
public int getX() {
return 0;
}
@Override
public void setX(int x) {
}
@Override
public int getY() {
return 0;
}
@Override
public void setY(int y) {
}
@Override
public int getWidth() {
return 0;
}
@Override
public void setWidth(int w) {
}
@Override
public int getHeight() {
return 0;
}
@Override
public void setHeight(int h) {
}
@Override
public void setLocation(int x, int y) {
}
@Override
public void setSize(int w, int h) {
}
@Override
public void setLayout(int x, int y, int w, int h) {
}
@Override
public Object getTag() {
return null;
}
@Override
public void setTag(Object tag) {
}
@Override
public String getDescription() {
return null;
}
@Override
public void setDescription(String desc) {
}
@Override
public int compareTo(IGObject o) {
return 0;
}
@Override
public String getLabel() {
return null;
}
/**
* Connects the two given elements as predecessor and successor.
* @param predecessor
* @param successor
*/
public static void connectElements(AbstractBPMNElement predecessor, AbstractBPMNElement successor){
if (predecessor != null && successor != null) {
predecessor.addSuccessor(successor);
successor.addPredecessor(predecessor);
}
}
/**
* Disconnects the two given elements as predecessor and successor.
* @param predecessor
* @param successor
*/
public static void disconnectElements(AbstractBPMNElement predecessor, AbstractBPMNElement successor){
if (predecessor != null && successor != null) {
predecessor.removeSuccessor(successor);
successor.removePredecessor(predecessor);
}
}
/**
* Returns all the elements, that have monitoring points.
* @param elements
* @return
*/
public static List<AbstractBPMNElement> getElementsWithMonitoringPoints(List<AbstractBPMNElement> elements) {
Set<AbstractBPMNElement> monitorableElements = new HashSet<AbstractBPMNElement>();
for(AbstractBPMNElement element : elements){
if(element.hasMonitoringPoints()){
monitorableElements.add(element);
}
}
return new ArrayList<AbstractBPMNElement>(monitorableElements);
}
}