/*******************************************************************************
* Copyright 2014 Miami-Dade County
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.sharegov.cirm.event;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hypergraphdb.util.Pair;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLLiteral;
import org.semanticweb.owlapi.model.OWLNamedIndividual;
import org.semanticweb.owlapi.reasoner.OWLReasoner;
import org.sharegov.cirm.OWL;
import mjson.Json;
import static org.sharegov.cirm.OWL.*;
/**
* <p>
* A singleton that manages dispatching of events defined in the ontology. Events are dispatched
* according to the type of entity they are about and the type of change. Each (entity type, change
* type) combination pair can have a set of event triggers invoked to process the event.
* </p>
*
* <p>
* A bit tired to think this through properly, but: the requirements of always having an entity
* and a change type is a bit too restrictive for a general event dispatcher. Even if we do
* have them, defining them in the ontology to do the proper (entity, change)->handler dispatch
* is a bit cumbersome. Sometimes you just want to say: here fire this event and pass in that
* Json. So I've added a means to do that with the eventTriggers map and an overloaded dispatch
* method that still takes an entity and change type as arguments because the EventTrigger interface
* is defined that way.
* </p>
* @author boris
*
*/
public class EventDispatcher
{
static EventDispatcher instance = null;
public synchronized static EventDispatcher get()
{
if (instance != null)
return instance;
instance = new EventDispatcher();
//OWLOntology O = OWL.ontology(Refs.topOntologyIRI.resolve());
OWLReasoner R = reasoner();
Set<OWLNamedIndividual> eventSet = R.getInstances(owlClass("EntityChangeEvent"),
false).getFlattened();
for (OWLNamedIndividual e : eventSet)
{
// Each EntityChangeEvent is configured with the entity types that it should be
// triggered for as a 'hasQueryExpression' data property, a list of implementation
// singletons that should be instantiated and invoked as event triggers and a list
// of changes that should trigger the event.
Set<OWLClass> types = new HashSet<OWLClass>();
for (OWLLiteral expr : reasoner().getDataPropertyValues(e, dataProperty("hasQueryExpression")))
{
String typeQuery = expr.getLiteral();
types.addAll(querySubsumedClasses(typeQuery, ontology()));
}
if (types.isEmpty())
types.add(dataFactory().getOWLThing());
types.remove(dataFactory().getOWLNothing());
Set<OWLNamedIndividual> triggers = OWL.objectProperties(e, "hasImplementation");
for (OWLClass cl : types)
for (OWLNamedIndividual chtype : OWL.objectProperties(e, "hasChangeType"))
for (OWLNamedIndividual trigger : triggers)
instance.addTrigger(instance.changeTriggers,
new Pair<OWLClass, OWLNamedIndividual>(cl, chtype), trigger);
for (OWLNamedIndividual trigger : triggers)
instance.addTrigger(instance.eventTriggers, e, trigger);
}
return instance;
}
Map<Pair<OWLClass, OWLNamedIndividual>, List<EventTrigger>> changeTriggers =
new HashMap<Pair<OWLClass, OWLNamedIndividual>, List<EventTrigger>>();
Map<OWLNamedIndividual, List<EventTrigger>> eventTriggers =
new HashMap<OWLNamedIndividual, List<EventTrigger>>();
public <KeyType> EventDispatcher addTrigger(Map<KeyType, List<EventTrigger>> map,
KeyType key,
OWLNamedIndividual trigger)
{
List<EventTrigger> L = map.get(key);
if (L == null)
{
L = new ArrayList<EventTrigger>();
map.put(key, L);
}
try
{
EventTrigger t = (EventTrigger)Class.forName(trigger.getIRI().getFragment()).newInstance();
L.add(t);
}
catch (Throwable t)
{
throw new RuntimeException(t);
}
return this;
}
/**
* <p>
* Dispatch a change event about a given individual calling only {@link EventTrigger}s for
* the specified type.
* </p>
*
* @param entity The individual that changed.
* @param entityType The type of interest - only event triggers associated with this type will be
* invoked even if the individual is also classified to be of other types.
* @param data An arbitrary piece of data to pass to the trigger.
* @param changeType The type of change.
* @return <code>this</code>
*/
public EventDispatcher dispatch(OWLNamedIndividual entity,
OWLClass entityType,
Json data,
OWLNamedIndividual changeType)
{
List<EventTrigger> L = changeTriggers.get(new Pair<OWLClass, OWLNamedIndividual>(entityType, changeType));
if (L != null) for (EventTrigger trigger : L)
trigger.apply(entity, changeType, data);
return this;
}
public EventDispatcher dispatch(OWLNamedIndividual event,
OWLNamedIndividual entity,
OWLNamedIndividual changeType,
Json data)
{
List<EventTrigger> L = eventTriggers.get(event);
if (L != null) for (EventTrigger trigger : L)
trigger.apply(entity, changeType, data);
return this;
}
/**
* <p>
* Iteratively call <code>dispatch(entity, type, null, changeType)</code> for
* each <code>type</code> the given <code>entity</code> is classified under.
* </p>
* @return <code>this</code>
*/
public EventDispatcher dispatch(OWLNamedIndividual entity, OWLNamedIndividual changeType)
{
//OWLOntology O = MetaService.get().getMetaOntology();
OWLReasoner R = reasoner();
for (OWLClass type : R.getTypes(entity, false).getFlattened())
{
dispatch(entity, type, null, changeType);
}
return this;
}
}