package sushi.event;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Query;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.eclipse.persistence.annotations.Index;
import sushi.event.attribute.SushiAttribute;
import sushi.event.collection.SushiMapTree;
import sushi.notification.SushiNotificationForEvent;
import sushi.persistence.Persistable;
import sushi.persistence.Persistor;
import sushi.process.SushiProcessInstance;
import sushi.visualisation.SushiTimePeriodEnum;
/**
* Representation of an event
*/
@Entity
@Table(name = "Event")
public class SushiEvent extends Persistable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
protected int ID;
@Temporal(TemporalType.TIMESTAMP)
protected Date timestamp = null;
// holds attributes and values in a tree structure
@OneToOne(cascade={CascadeType.PERSIST, CascadeType.REMOVE})
@JoinColumn(name="MapTreeID")
protected SushiMapTree<String, Serializable> values;
@ManyToMany(mappedBy="events")
List<SushiProcessInstance> processInstances;
@Index @ManyToOne
private SushiEventType eventType;
/**
* Default-Constructor for JPA.
*/
private SushiEvent() {
this.ID = 0;
this.eventType = null;
this.timestamp = null;
this.values = new SushiMapTree<String, Serializable>();
this.processInstances = new ArrayList<SushiProcessInstance>();
}
/**
* Creates an event with a timestamp
* @param timestamp
*/
private SushiEvent(Date timestamp) {
this();
this.timestamp = timestamp;
}
/**
* Creates an event with event type and timestamp.
* @param eventType
* @param timestamp
*/
public SushiEvent(SushiEventType eventType, Date timestamp) {
this(timestamp);
this.eventType = eventType;
}
/**
* Creates an event with timestamp and attributes.
* @param timestamp
* @param values
*/
public SushiEvent(Date timestamp, SushiMapTree<String, Serializable> values) {
this(timestamp);
this.values = values;
}
/**
* Creates an event with event type, timestamp and attributes.
* @param eventType
* @param timestamp
* @param values
*/
public SushiEvent(SushiEventType eventType, Date timestamp, SushiMapTree<String, Serializable> values) {
this(eventType, timestamp);
this.values = values;
}
public boolean addProcessInstance(SushiProcessInstance processInstance) {
if(!processInstances.contains(processInstance)) {
return processInstances.add(processInstance);
}
return false;
}
public boolean addAllProcessInstances(List<SushiProcessInstance> processInstances) {
boolean allInserted = true;
boolean inserted;
for(SushiProcessInstance processInstance : processInstances){
inserted = this.addProcessInstance(processInstance);
allInserted = allInserted ? inserted : false;
}
return allInserted;
}
public boolean removeProcessInstance(SushiProcessInstance processInstance) {
return processInstances.remove(processInstance);
}
@Override
public boolean equals(Object o) {
SushiEvent e;
if (o instanceof SushiEvent) {
e = (SushiEvent) o;
if (this.ID == e.getID() && this.timestamp.compareTo(e.getTimestamp()) == 0) {
return true;
}
}
return false;
}
@Override
public String toString() {
StringBuffer eventText = new StringBuffer();
eventText.append("Event with ID=" + this.ID);
eventText.append(", Timestamp=" + this.timestamp.toString());
eventText.append(", Event Type=" + this.eventType);
Map<String, Serializable> values = this.getValues();
Iterator<String> valueIterator = values.keySet().iterator();
while (valueIterator.hasNext()) {
String valueKey = valueIterator.next();
eventText.append(", " + valueKey + "=" + values.get(valueKey));
}
return eventText.toString();
}
public String shortenedString() {
StringBuffer eventText = new StringBuffer();
eventText.append("Event with ID=" + this.ID);
eventText.append("of Event Type=" + this.eventType);
return eventText.toString();
}
public String fullEvent() {
String eventText = "Event with ID: " + this.ID + " time: " + this.timestamp.toString();
eventText = eventText + System.getProperty("line.separator") + this.getValues();
return eventText;
}
public boolean isHierarchical() {
return values.isHierarchical();
}
/**
* set the Eventtyp of all given Events to the specified Eventtyp
* @param events
* @param eventType
* @return
*/
public static List<SushiEvent> setEventType(List<SushiEvent> events, SushiEventType eventType) {
for (SushiEvent event: events) {
event.setEventType(eventType);
}
return events;
}
//Getter and Setter
public SushiMapTree<String, Serializable> getValues() {
return values;
}
public void setValues(SushiMapTree<String, Serializable> values) {
this.values = values;
}
public Date getTimestamp() {
return timestamp;
}
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
public int getID() {
return ID;
}
public void setID(int iD) {
ID = iD;
}
public SushiEventType getEventType() {
return eventType;
}
public void setEventType(SushiEventType eventType) {
this.eventType = eventType;
}
public List<SushiProcessInstance> getProcessInstances() {
// because processInstances is of type IndirectList (JPA)
return new ArrayList<SushiProcessInstance>(processInstances);
}
public void setProcessInstances(List<SushiProcessInstance> processInstance) {
this.processInstances = processInstance;
}
//JPA-Methods
/**
* Method returns events, where the specified column name has the specified value.
*/
private static List<SushiEvent> findByAttribute(String columnName, Object value){
Query query = Persistor.getEntityManager().createNativeQuery("" +
"SELECT * FROM Event " +
"WHERE " + columnName + " = '" + value + "'", SushiEvent.class);
return query.getResultList();
}
public static List<SushiEvent> findByEventTypeAndAttributeExpressionsAndValues(SushiEventType eventType, Map<String, Serializable> attributeExpressionsAndValues){
StringBuffer sb = new StringBuffer();
sb.append("" +
"SELECT * FROM Event JOIN SushiMapTree_SushiMapTreeElements mte " +
"ON mte.SushiMapTree_SushiMapID=Event.MapTreeID " +
"JOIN SushiMapTree_SushiMapTreeRootElements mtre " +
"ON mtre.SushiMapTree_SushiMapID = Event.MapTreeID " +
"JOIN SushiMapElement me " +
"ON (me.ID = mtre.treeRootElements_ID OR me.ID = mtre.treeRootElements_ID) " +
"WHERE EVENTTYPE_ID = '" + eventType.getID() + "'");
Iterator<String> iterator = attributeExpressionsAndValues.keySet().iterator();
if (iterator.hasNext()) {
sb.append(" AND ");
}
while (iterator.hasNext()) {
String attributeExpression = iterator.next();
Serializable value = attributeExpressionsAndValues.get(attributeExpression);
if (value instanceof Date) {
sb.append("(me.MapKey = '" + attributeExpression + "' AND me.MapValue = {ts '" + (new SimpleDateFormat("yyyy-MM-dd hh:mm:ss")).format((Date) value) + "'})");
} else {
sb.append("(me.MapKey = '" + attributeExpression + "' AND me.MapValue = '" + value + "')");
}
if (iterator.hasNext()) {
sb.append(" OR ");
}
}
Query query = Persistor.getEntityManager().createNativeQuery(sb.toString(), SushiEvent.class);
return query.getResultList();
}
public static Serializable findValueByEventTypeAndAttributeExpressionsAndValues(SushiEventType eventType, String attributeExpressionOfValue, Map<String, Serializable> attributeExpressionsAndValuesForSearch){
StringBuffer sb = new StringBuffer();
sb.append("" +
"SELECT me.MapValue FROM Event " +
"INNER JOIN SushiMapTree_SushiMapTreeElements mte " +
"ON (mte.SushiMapTree_SushiMapID = Event.MapTreeID) " +
"INNER JOIN SushiMapElement me " +
"ON (me.ID = mte.treeElements_ID) " +
"WHERE EVENTTYPE_ID = '" + eventType.getID() + "' " +
"AND me.MapKey = '" + attributeExpressionOfValue + "'");
Iterator<String> iterator = attributeExpressionsAndValuesForSearch.keySet().iterator();
int indexOfMapPair = 1;
if (iterator.hasNext()) {
sb.append(" AND mte.SushiMapTree_SushiMapID IN (SELECT id" + indexOfMapPair + ".SushiMapTree_SushiMapID FROM");
}
while (iterator.hasNext()) {
String attributeExpression = iterator.next();
Serializable value = attributeExpressionsAndValuesForSearch.get(attributeExpression);
if (indexOfMapPair > 1) {
sb.append(" INNER JOIN");
}
if (value instanceof Date) {
sb.append(" " +
"(SELECT SushiMapTree_SushiMapID FROM SushiMapTree_SushiMapTreeElements " +
"WHERE treeElements_ID IN " +
"(SELECT ID FROM SushiMapElement " +
"WHERE MapKey = '" + attributeExpression + "' " +
"AND me.MapValue = {ts '" + (new SimpleDateFormat("yyyy-MM-dd hh:mm:ss")).format((Date) value) + "'})" +
") id" + indexOfMapPair);
} else {
sb.append(" " +
"(SELECT SushiMapTree_SushiMapID FROM SushiMapTree_SushiMapTreeElements " +
"WHERE treeElements_ID IN " +
"(SELECT ID FROM SushiMapElement " +
"WHERE MapKey = '" + attributeExpression + "' " +
"AND MapValue = '" + value + "')" +
") id" + indexOfMapPair);
}
if (indexOfMapPair > 1) {
sb.append(" ON id" + (indexOfMapPair - 1) + ".SushiMapTree_SushiMapID = id" + indexOfMapPair + ".SushiMapTree_SushiMapID");
}
indexOfMapPair++;
}
sb.append(")");
Query query = Persistor.getEntityManager().createNativeQuery(sb.toString());
if (query.getResultList().isEmpty()) {
return null;
}
return (Serializable) query.getResultList().get(0);
}
private static List<SushiEvent> findByAttributeGreaterThan(String columnName, String value) {
Query query = Persistor.getEntityManager().createNativeQuery("" +
"SELECT * FROM Event " +
"WHERE " + columnName + " > '" + value + "'", SushiEvent.class);
return query.getResultList();
}
private static List<SushiEvent> findByAttributeLessThan(String columnName, String value) {
Query query = Persistor.getEntityManager().createNativeQuery("" +
"SELECT * FROM Event " +
"WHERE " + columnName + " < '" + value + "'", SushiEvent.class);
return query.getResultList();
}
/**
* returns SushiEvents which have the specified attribut/value pair
* @param key
* @param value
* @return
*/
public static List<SushiEvent> findByValue(String key, Serializable value){
HashMap<String, Serializable> attributes = new HashMap<String, Serializable>();
attributes.put(key, value);
return findByValues(attributes);
}
/**
* returns SushiEvents which have all spezified attribute/value pairs
* @param eventAttributes
* @return
*/
public static List<SushiEvent> findByValues(Map<String, Serializable> eventAttributes){
List<SushiEvent> allEvents = SushiEvent.findAll();
List<SushiEvent> matchingEvents = new ArrayList<SushiEvent>();
for(SushiEvent event : allEvents){
if(event.containsAllValues(eventAttributes)){
matchingEvents.add(event);
}
}
return matchingEvents;
}
/**
* checks if the Event have the given attributes/values
* @param eventAttributes
* @return
*/
private boolean containsAllValues(Map<String, Serializable> eventAttributes) {
for(Entry<String, Serializable> comparedValue : eventAttributes.entrySet()){
Object value = values.get(comparedValue.getKey());
if(value == null || !values.containsKey(comparedValue.getKey()) || !value.equals(comparedValue.getValue())){
return false;
}
}
return true;
}
/**
* returns SushiEvents from the given SushiEventtyp
* @param eventType
* @return
*/
public static List<SushiEvent> findByEventType(SushiEventType eventType){
Query query = Persistor.getEntityManager().createNativeQuery("SELECT * FROM Event WHERE EVENTTYPE_ID = '" + eventType.getID() + "'", SushiEvent.class);
return query.getResultList();
}
/**
* returns SushiEvents from the given SushiEventtyp and have a timestamp which lies maximal "period" in the past
* @param eventType
* @param period
* @return
*/
public static List<SushiEvent> findByEventTypeAndTime(SushiEventType eventType, SushiTimePeriodEnum period){
if (period == SushiTimePeriodEnum.INF) {
return findByEventType(eventType);
}
Date start = period.getStartTime();
return findBetween(start, new Date(), eventType);
}
/**
* returns number SushiEvents from the given Eventtyp
* @param eventType
* @return
*/
public static long getNumberOfEventsByEventType(SushiEventType eventType){
Query query = Persistor.getEntityManager().createNativeQuery("SELECT count(*) FROM Event WHERE EVENTTYPE_ID = '" + eventType.getID() + "'");
long value = (Long) query.getSingleResult();
return value;
}
/**
* Returns the number of events in the database.
* @return overall number of SushiEvents
*/
public static long getNumberOfEvents(){
Query query = Persistor.getEntityManager().createNativeQuery("SELECT count(*) FROM Event");
long value = (Long) query.getSingleResult();
return value;
}
/**
* returns SushiEvents which belongs to the specified processInstance
* @param processInstance
* @return
*/
public static List<SushiEvent> findByProcessInstance (SushiProcessInstance processInstance){
Query query = Persistor.getEntityManager().createNativeQuery("" +
"Select * " +
"FROM Event " +
"WHERE ID IN (" +
"Select events_ID " +
"FROM ProcessInstance_Event " +
"WHERE processInstances_ID = '" + processInstance.getID()+ "')", SushiEvent.class);
return query.getResultList();
}
/**
* returns SushiEvent with the specified ID
* @param ID
* @return
*/
public static SushiEvent findByID(int ID){
List<SushiEvent> events = findByAttribute("ID", Integer.toString(ID));
if(!events.isEmpty()) {
return events.get(0);
} else {
return null;
}
}
/**
* returns Events which have an ID greater than the given ID
* @param ID
* @return
*/
public static List<SushiEvent> findByIDGreaterThan(int ID){
return findByAttributeGreaterThan("ID", Integer.toString(ID));
}
/**
* returns Events which have an ID less than the given ID
* @param ID
* @return
*/
public static List<SushiEvent> findByIDLessThan(int ID){
return findByAttributeLessThan("ID", Integer.toString(ID));
}
/**
* returns Events which have a timestamp between the given Dates
* @param startDate
* @param endDate
* @return
*/
public static List<SushiEvent> findBetween(Date startDate, Date endDate){
return findBetween(startDate, endDate, null);
}
/**
* returns Events from the given Eventty and having a timestamp between the given Dates
* @param startDate
* @param endDate
* @return
*/
@SuppressWarnings("unchecked")
public static List<SushiEvent> findBetween(Date startDate, Date endDate, SushiEventType eventType){
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS");
Query query;
if(eventType != null){
query = Persistor.getEntityManager().createNativeQuery("SELECT * FROM Event WHERE TIMESTAMP BETWEEN '" + dateFormat.format(startDate) + "' AND '" + dateFormat.format(endDate) + "' AND EVENTTYPE_ID ='" + eventType.getID() + "'", SushiEvent.class);
}
else{
query = Persistor.getEntityManager().createNativeQuery("SELECT * FROM Event WHERE TIMESTAMP BETWEEN '" + dateFormat.format(startDate) + "' AND '" + dateFormat.format(endDate) + "'", SushiEvent.class);
}
return query.getResultList();
}
/**
* @return all SushiEvents
*/
public static List<SushiEvent> findAll() {
Query q = Persistor.getEntityManager().createQuery("select t from SushiEvent t");
return q.getResultList();
}
@Override
public SushiEvent save() {
return (SushiEvent) super.save();
}
@Override
public SushiEvent merge() {
return (SushiEvent) super.merge();
}
/**
* saves the given SushiEvents
* @param events
* @return
*/
public static List<SushiEvent> save(List<SushiEvent> events) {
try {
Persistor.getEntityManager().getTransaction().begin();
for (SushiEvent event : events) {
Persistor.getEntityManager().persist(event);
}
Persistor.getEntityManager().getTransaction().commit();
return events;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
public SushiEvent remove() {
//dieses Event aus Prozessinstanzen loeschen
for(SushiProcessInstance processInstance : this.getProcessInstances()){
processInstance.removeEvent(this);
if(processInstance.getTimerEvent() != null && processInstance.getTimerEvent().equals(this)){
processInstance.setTimerEvent(null);
if(processInstance.getProcess().getTimeCondition() != null){
processInstance.getProcess().getTimeCondition().removeTimerEvent(this);
processInstance.getProcess().getTimeCondition().merge();
}
}
processInstance.merge();
}
//Notifications fuer dieses Event loeschen
for (SushiNotificationForEvent notification : SushiNotificationForEvent.findForEvent(this)) {
notification.remove();
}
return (SushiEvent) super.remove();
}
/**
* Deletes the specified events from the database.
* @return
*/
public static boolean remove(ArrayList<SushiEvent> events) {
boolean removed = true;
for(SushiEvent event : events){
removed = (event.remove() != null);
}
return removed;
}
/**
* deletes all SushiEvents
*/
public static void removeAll() {
for(SushiEvent actualEvent : SushiEvent.findAll()){
actualEvent.remove();
}
}
public static ArrayList<String> findAllEventAttributes() {
List<SushiEventType> eventTypes = SushiEventType.findAll();
Set<String> attributeSet = new HashSet<String>();
for (SushiEventType actualEventType : eventTypes) {
for (SushiAttribute attribute : actualEventType.getValueTypes()) {
attributeSet.add(attribute.getAttributeExpression());
}
}
return new ArrayList<String>(attributeSet);
}
/**
* returns distinct values of the given attribute of the given eventtyp
*/
public static List<String> findDistinctValuesOfAttributeOfType(String attributeName, SushiEventType type) {
Query query = Persistor.getEntityManager().createNativeQuery("SELECT DISTINCT me.MapValue " +
"FROM Event JOIN SushiMapTree_SushiMapTreeElements mte " +
"ON mte.SushiMapTree_SushiMapID=Event.MapTreeID " +
"JOIN SushiMapTree_SushiMapTreeRootElements mtre " +
"ON mtre.SushiMapTree_SushiMapID = Event.MapTreeID " +
"JOIN SushiMapElement me " +
"ON (me.ID = mtre.treeRootElements_ID OR me.ID = mtre.treeRootElements_ID) " +
"WHERE EVENTTYPE_ID = '" + type.getID() + "' " +
"AND me.MapKey = '" + attributeName + "'");
return query.getResultList();
}
/**
* return the number of the repetition of the value in the specified eventtyp and attribute
*/
public static long findNumberOfAppearancesByAttributeValue(String attributeName, String value, SushiEventType type) {
Query query = Persistor.getEntityManager().createNativeQuery("SELECT count(DISTINCT Event.ID) " +
"FROM Event JOIN SushiMapTree_SushiMapTreeElements mte " +
"ON mte.SushiMapTree_SushiMapID=Event.MapTreeID " +
"JOIN SushiMapTree_SushiMapTreeRootElements mtre " +
"ON mtre.SushiMapTree_SushiMapID = Event.MapTreeID " +
"JOIN SushiMapElement me " +
"ON (me.ID = mtre.treeRootElements_ID OR me.ID = mtre.treeRootElements_ID) " +
"WHERE EVENTTYPE_ID = '" + type.getID() + "' " +
"AND me.MapKey = '" + attributeName + "' "+
"AND me.MapValue = '" + value + "'");
return (long) query.getSingleResult();
}
/**
* returns minimal value of the given attribute in the specified eventtyp
*/
public static long getMinOfAttributeValue(String attribute, SushiEventType eventType) {
List<String> values = SushiEvent.findDistinctValuesOfAttributeOfType(attribute, eventType);
if (values.size() > 0) {
int min = Integer.parseInt(values.get(0));
for (String value : values) {
int akt = Integer.parseInt(value);
if (akt < min ) min = akt;
}
return min;
}
return (Long) null;
}
/**
* returns maximal value of the given attribute in the specified eventtyp
*/
public static long getMaxOfAttributeValue(String attribute, SushiEventType eventType) {
List<String> values = SushiEvent.findDistinctValuesOfAttributeOfType(attribute, eventType);
if (values.size() > 0) {
int max = Integer.parseInt(values.get(0));
for (String value : values) {
int akt = Integer.parseInt(value);
if (akt > max ) max = akt;
}
return max;
}
return (Long) null;
}
}