/**
* Copyright (C) 2008 - 2014 52°North Initiative for Geospatial Open Source
* Software GmbH
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* If the program is linked with libraries which are licensed under one of
* the following licenses, the combination of the program with the linked
* library is not considered a "derivative work" of the program:
*
* - Apache License, version 2.0
* - Apache Software License, version 1.0
* - GNU Lesser General Public License, version 3
* - Mozilla Public License, versions 1.0, 1.1 and 2.0
* - Common Development and Distribution License (CDDL), version 1.0
*
* Therefore the distribution of the program linked with libraries licensed
* under the aforementioned licenses, is permitted by the copyright holders
* if the distribution is compliant with both the GNU General Public
* icense version 2 and the aforementioned licenses.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*/
/**
* Part of the diploma thesis of Thomas Everding.
* @author Thomas Everding
*/
package org.n52.ses.api.event;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import org.n52.ses.api.common.GlobalConstants;
import org.n52.ses.api.ws.INotificationMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Representation of events. Used in the esper engine for every event.
*
* @author Thomas Everding, Matthes Rieke
*
*/
public class MapEvent implements Map<String, Object>, Serializable {
/**
*
*/
private static final long serialVersionUID = -887256612214404797L;
private static final Logger logger = LoggerFactory.getLogger(MapEvent.class);
/*
* Logger instance for this class
*/
/**
* reserved key for the start timestamp
*/
public static final String START_KEY = "startTime";
/**
* reserved key for the end timestamp
*/
public static final String END_KEY = "endTime";
/**
* reserved key for the valid time of the event (from start to end)
*/
public static final String VALID_TIME_KEY = "validTime";
/**
* reserved key for the causal vector
*/
public static final String CAUSALITY_KEY = "causality";
/**
* reserved key for the value of an event
*/
public static final String VALUE_KEY = "value";
/**
* key for the value of an event as string
*/
public static final String STRING_VALUE_KEY = "stringValue";
/**
* key for the value of an event as double
*/
public static final String DOUBLE_VALUE_KEY = "doubleValue";
/**
* reserved key for the geometry of an event
*/
public static final String GEOMETRY_KEY = "geometry";
/**
* reserved key for the sensor ID of an event
*/
public static final String SENSORID_KEY = "sensorID";
/**
* key for the reflection property back to this {@link MapEvent}
*/
public static final String THIS_KEY = "this";
/**
* key for the original message
*/
public static final String ORIGNIAL_MESSAGE_KEY = "originalMessage";
/**
* key for the feature of interest ID
*/
public static final String FOI_ID_KEY = "foiID";
/**
* key for the result value
*/
public static final String RESULT_KEY = "result";
/**
* Key for the causal ancestor (only used in select functions and thus in EventBeans from esper)
*/
public static final String CAUSAL_ANCESTOR_1_KEY = "ancestor1";
/**
* Key for the causal ancestor (only used in select functions and thus in EventBeans from esper)
*/
public static final String CAUSAL_ANCESTOR_2_KEY = "ancestor2";
/**
* Key for the observed property
*/
public static final String OBSERVED_PROPERTY_KEY = "observedProperty";
/**
* Key for the generic content of an XML node
*/
public static final String CONTENT_KEY = "content";
/**
* Key for the code space of an gml:identifier element
*/
public static final String IDENTIFIER_CODESPACE_KEY = "identifierCodeSpace";
/**
* Key for the value of an gml:identifier element
*/
public static final String IDENTIFIER_VALUE_KEY = "gml:identifier";
/**
* Key for the aixm:designator value
*/
public static final String AIXM_DESIGNATOR_KEY = "designator";
/**
* Key for feature type
*/
public static final String FEATURE_TYPE_KEY = "featureType";
/**
* Key for the status
*/
public static final String STAUS_KEY = "status";
/**
* Key for the lower limit of a volume
*/
public static final String LOWER_LIMIT_KEY = "lowerLimit";
/**
* Key for the upper limit of a volume
*/
public static final String UPPER_LIMIT_KEY = "upperLimit";
/**
* Key for the reservation phase
*/
public static final String RESERVATION_PHASE_KEY = "reservationPhase";
private HashMap<String, Object> map;
private long start;
private long end;
private INotificationMessage originalMessage;
/**
*
* Constructor
*
* @param startTime start time in ms
* @param endTime end time in ms (can be <= startTime for end = start)
*
*/
public MapEvent(long startTime, long endTime) {
this.map = new HashMap<String, Object>();
this.start = startTime;
if (endTime > startTime) {
this.end = endTime;
}
else {
this.end = this.start;
}
this.initialize();
}
/**
* Constructor creating a duplication of the
* passed MapEvent.
*
* @param duplicate the MapEvent to be duplicated
*/
public MapEvent(MapEvent duplicate) {
this.map = new HashMap<String, Object>(duplicate.map);
this.start = duplicate.start;
this.end = duplicate.end;
}
/**
* initializes the event map
*/
private void initialize() {
//create causal vector
Vector<MapEvent> causality = new Vector<MapEvent>();
this.put(CAUSALITY_KEY,causality);
//set start and end time
this.put(START_KEY, this.start);
this.put(END_KEY, this.end);
//set this
this.put(THIS_KEY, this);
//set valid time
this.put(MapEvent.VALID_TIME_KEY, this.generateValidTimeValue());
}
/**
* adds a causal ancestor of this event
*
* @param event the causal ancestor
*/
@SuppressWarnings("unchecked")
public void addCausalAncestor(MapEvent event) {
// logger.info("adding causal ancestor:\n" + event);
((Vector<MapEvent>)this.get(CAUSALITY_KEY)).add(event);
}
@Override
public void clear() {
this.map.clear();
//initialize
this.initialize();
}
@Override
public boolean containsKey(Object key) {
return this.map.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return this.map.containsValue(value);
}
@Override
public Set<java.util.Map.Entry<String, Object>> entrySet() {
return this.map.entrySet();
}
@Override
public Object get(Object key) {
return this.map.get(key);
}
@Override
public boolean isEmpty() {
//a map event is never empty
return false;
}
@Override
public Set<String> keySet() {
return this.map.keySet();
}
@Override
public Object put(String key, Object value) {
if (key.equals(ORIGNIAL_MESSAGE_KEY)) {
if (value instanceof INotificationMessage) {
this.originalMessage = (INotificationMessage) value;
}
}
else if (key.equals(VALUE_KEY)) {
//check if we must add id also as doubleValue and/or stringValue
try {
Double doubleTest = Double.parseDouble(value.toString());
//parsing worked: double value
this.map.put(DOUBLE_VALUE_KEY, doubleTest);
this.map.put(STRING_VALUE_KEY, ""+ doubleTest);
} catch (NumberFormatException e) {
//no double value, just string
this.map.put(STRING_VALUE_KEY, value.toString());
}
}
else if (key.equals(START_KEY)) {
//set value
this.start = Long.parseLong(value.toString());
//update valid time
this.map.put(VALID_TIME_KEY, this.generateValidTimeValue());
}
else if (key.equals(END_KEY)) {
//set value
this.end = Long.parseLong(value.toString());
//update valid time
this.map.put(VALID_TIME_KEY, this.generateValidTimeValue());
}
return this.map.put(key, value);
}
@Override
public void putAll(Map<? extends String, ? extends Object> m) {
//iterate through all entries to use local put method
Set<? extends String> keys = m.keySet();
for (String key : keys) {
this.put(key, m.get(key));
}
}
@Override
public Object remove(Object key) {
//causality and start/end time are not to be removed
if (key.equals(START_KEY)
|| key.equals(END_KEY)
|| key.equals(CAUSALITY_KEY)
|| key.equals(GEOMETRY_KEY)
|| key.equals(SENSORID_KEY)
|| key.equals(THIS_KEY)) {
return null;
}
return this.map.remove(key);
}
@Override
public int size() {
return this.map.size();
}
@Override
public Collection<Object> values() {
return this.map.values();
}
/**
*
* @return the original message of this event
*/
public INotificationMessage getOriginalMessage() {
return this.originalMessage;
}
@SuppressWarnings("unchecked")
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("MapEvent={");
for (String key : this.map.keySet()) {
if (!key.equals(MapEvent.THIS_KEY) && !key.equals(MapEvent.ORIGNIAL_MESSAGE_KEY)) {
if (this.map.get(key) instanceof Map<?, ?>) {
//recursion
sb.append(key + "={" + this.writeMapContent((Map<String, Object>) this.map.get(key), ", "));
sb.append("}, ");
}
else if (this.map.get(key) instanceof Vector<?>) {
//vector recursion
sb.append(key + "={");
sb.append(this.writeVectorContent((Vector<Object>) this.map.get(key), ", "));
sb.append("}, ");
}
else {
sb.append(key +"="+ this.map.get(key) +", ");
}
}
}
sb.delete(sb.length() - 2, sb.length());
sb.append("}");
return sb.toString();
}
/**
* recursively makes nice strings from maps
* @param map the map to print
* @param indention add one " |\t" per recursion
* @return pretty string of the map
*/
@SuppressWarnings("unchecked")
private String writeMapContent(Map<String, Object> m, String indention) {
StringBuilder sb = new StringBuilder();
for (String s : m.keySet()) {
if (s.equals(MapEvent.THIS_KEY)) {
//ignore this reference
}
else if (m.get(s) instanceof Map<?, ?>) {
//map recursion
sb.append(indention + s + "={");
// sb.append(indention + "ignored Map\n");
sb.append(this.writeMapContent((Map<String, Object>) m.get(s), indention + ", "));
sb.append("}");
}
else if (m.get(s) instanceof Vector<?>) {
//vector recursion
sb.append(indention + s + "={");
sb.append(this.writeVectorContent((Vector<Object>) m.get(s), indention + ", "));
sb.append("}");
}
else {
sb.append(indention + s + "=" + m.get(s) + ", ");
}
}
return sb.toString();
}
/**
*
* @return the actual valid time value
*/
private String generateValidTimeValue() {
return this.start + GlobalConstants.TEMPORAL_INTERVAL_SEPARATOR + this.end;
}
/**
* recursively makes nice strings from vectors
* @param vector the vector to print
* @param indention indention add one " |\t" per recursion
* @return pretty string of the vector
*/
@SuppressWarnings("unchecked")
private String writeVectorContent(Vector<Object> vector, String indention) {
StringBuilder sb = new StringBuilder();
Object item;
for (int i = 0; i < vector.size(); i++) {
item = vector.get(i);
if (item instanceof Map<?, ?>) {
//map recursion
sb.append(indention + i + "={");
sb.append(this.writeMapContent((Map<String, Object>) item, indention + ", "));
sb.append("}");
}
else if (item instanceof Vector<?>) {
//vector recursion
sb.append(indention + i + "={");
sb.append(this.writeVectorContent((Vector<Object>) item, indention + ", "));
sb.append("}");
}
else {
//append content
sb.append(indention + i + "=" + item.toString());
}
}
return sb.toString();
}
public byte[] serialize() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
return bos.toByteArray();
}
public static MapEvent deserialize(InputStream is) throws IOException {
ObjectInputStream ois = new ObjectInputStream(is);
Object o;
try {
o = ois.readObject();
return (MapEvent) o;
} catch (ClassNotFoundException e) {
logger.warn("Could not deserialize MapEvent object", e);
throw new IOException(e);
}
}
}