package de.lmu.ifi.dbs.elki.database;
/*
This file is part of ELKI:
Environment for Developing KDD-Applications Supported by Index-Structures
Copyright (C) 2015
Ludwig-Maximilians-Universität München
Lehr- und Forschungseinheit für Datenbanksysteme
ELKI Development Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
import java.util.ArrayList;
import java.util.List;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreEvent;
import de.lmu.ifi.dbs.elki.database.datastore.DataStoreListener;
import de.lmu.ifi.dbs.elki.database.ids.DBIDRef;
import de.lmu.ifi.dbs.elki.database.ids.DBIDUtil;
import de.lmu.ifi.dbs.elki.database.ids.DBIDs;
import de.lmu.ifi.dbs.elki.database.ids.HashSetModifiableDBIDs;
import de.lmu.ifi.dbs.elki.result.Result;
import de.lmu.ifi.dbs.elki.result.ResultListener;
/**
* Class to manage database events such as insertions and removals.
*
* @author Elke Achtert
* @since 0.4.0
* @apiviz.has ResultListener
*/
public class DatabaseEventManager {
/**
* Holds the listeners for data store changes
*/
private List dataListenerList = new ArrayList<>();
/**
* Holds the listeners for result changes.
*/
private List resultListenerList = new ArrayList<>();
/**
* Indicates whether DataStoreEvents should be accumulated and fired as one
* event on demand.
*/
private boolean accumulateDataStoreEvents = false;
/**
* The type of the current DataStoreEvent to be accumulated.
*/
private Type currentDataStoreEventType = null;
/**
* Types for aggregation.
*
* @apiviz.exclude
*/
private enum Type {
INSERT, REMOVE, UPDATE
};
/**
* The objects that were changed in the current DataStoreEvent.
*/
private HashSetModifiableDBIDs dataStoreObjects;
/**
* Collects successive insertion, deletion or update events. The accumulated
* event will be fired when {@link #flushDataStoreEvents()} is called or a
* different event type occurs.
*
* @see #flushDataStoreEvents()
* @see DataStoreEvent
*/
public void accumulateDataStoreEvents() {
this.accumulateDataStoreEvents = true;
}
/**
* Fires all collected insertion, deletion or update events as one
* DataStoreEvent, i.e. notifies all registered DataStoreListener how the
* content of the database has been changed since
* {@link #accumulateDataStoreEvents()} was called.
*
* @see #accumulateDataStoreEvents
* @see DataStoreListener
* @see DataStoreEvent
*/
public void flushDataStoreEvents() {
DataStoreEvent e;
switch(currentDataStoreEventType){
case INSERT:
e = DataStoreEvent.insertionEvent(dataStoreObjects);
break;
case REMOVE:
e = DataStoreEvent.removalEvent(dataStoreObjects);
break;
case UPDATE:
e = DataStoreEvent.updateEvent(dataStoreObjects);
break;
default:
return;
}
for(int i = dataListenerList.size(); --i >= 0;) {
dataListenerList.get(i).contentChanged(e);
}
// reset
accumulateDataStoreEvents = false;
currentDataStoreEventType = null;
dataStoreObjects = null;
}
/**
* Adds a DataStoreListener
for a DataStoreEvent
* posted after the content of the database changes.
*
* @param l the listener to add
* @see #removeListener(DataStoreListener)
* @see DataStoreListener
* @see DataStoreEvent
*/
public void addListener(DataStoreListener l) {
dataListenerList.add(l);
}
/**
* Removes a DataStoreListener
previously added with
* {@link #addListener(DataStoreListener)}.
*
* @param l the listener to remove
* @see #addListener(DataStoreListener)
* @see DataStoreListener
* @see DataStoreEvent
*/
public void removeListener(DataStoreListener l) {
dataListenerList.remove(l);
}
/**
* Adds a ResultListener
to be notified on new results.
*
* @param l the listener to add
* @see #removeListener(ResultListener)
* @see ResultListener
* @see Result
*/
public void addListener(ResultListener l) {
resultListenerList.add(l);
}
/**
* Removes a ResultListener
previously added with
* {@link #addListener(ResultListener)}.
*
* @param l the listener to remove
* @see #addListener(ResultListener)
* @see ResultListener
* @see Result
*
*/
public void removeListener(ResultListener l) {
resultListenerList.remove(l);
}
/**
* Event when new objects are inserted.
*
* @param insertions the objects that have been inserted
*/
public void fireObjectsInserted(DBIDs insertions) {
fireObjectsChanged(insertions, Type.INSERT);
}
/**
* Event when a new object was inserted.
*
* @param insertion the object that was inserted
*/
public void fireObjectInserted(DBIDRef insertion) {
fireObjectChanged(insertion, Type.INSERT);
}
/**
* Event when objects have changed / were updated.
*
* @param updates the objects that have been updated
*/
public void fireObjectsUpdated(DBIDs updates) {
fireObjectsChanged(updates, Type.UPDATE);
}
/**
* Event when an object was changed / updated.
*
* @param update the object that was updated
*/
public void fireObjectsUpdated(DBIDRef update) {
fireObjectChanged(update, Type.UPDATE);
}
/**
* Event when objects were removed / deleted.
*
* @param deletions the objects that have been removed
*/
protected void fireObjectsRemoved(DBIDs deletions) {
fireObjectsChanged(deletions, Type.REMOVE);
}
/**
* Event when an objects was removed / deleted.
*
* @param deletion the object that has was removed
*/
protected void fireObjectRemoved(DBIDRef deletion) {
fireObjectChanged(deletion, Type.REMOVE);
}
/**
* Handles a DataStoreEvent with the specified type. If the current event type
* is not equal to the specified type, the events accumulated up to now will
* be fired first.
*
* The new event will be aggregated and fired on demand if
* {@link #accumulateDataStoreEvents} is set, otherwise all registered
* DataStoreListener
will be notified immediately that the
* content of the database has been changed.
*
* @param objects the objects that have been changed, i.e. inserted, deleted
* or updated
*/
private void fireObjectsChanged(DBIDs objects, Type type) {
// flush first
if(currentDataStoreEventType != null && !currentDataStoreEventType.equals(type)) {
flushDataStoreEvents();
}
if(accumulateDataStoreEvents) {
if(this.dataStoreObjects == null) {
this.dataStoreObjects = DBIDUtil.newHashSet();
}
this.dataStoreObjects.addDBIDs(objects);
currentDataStoreEventType = type;
return;
}
// Execute immediately:
DataStoreEvent e;
switch(type){
case INSERT:
e = DataStoreEvent.insertionEvent(objects);
break;
case REMOVE:
e = DataStoreEvent.removalEvent(objects);
break;
case UPDATE:
e = DataStoreEvent.updateEvent(objects);
break;
default:
return;
}
for(int i = dataListenerList.size(); --i >= 0;) {
dataListenerList.get(i).contentChanged(e);
}
}
/**
* Handles a DataStoreEvent with the specified type. If the current event type
* is not equal to the specified type, the events accumulated up to now will
* be fired first.
*
* The new event will be aggregated and fired on demand if
* {@link #accumulateDataStoreEvents} is set, otherwise all registered
* DataStoreListener
will be notified immediately that the
* content of the database has been changed.
*
* @param object the object that has been changed, i.e. inserted, deleted or
* updated
*/
private void fireObjectChanged(DBIDRef object, Type type) {
// flush first
if(currentDataStoreEventType != null && !currentDataStoreEventType.equals(type)) {
flushDataStoreEvents();
}
if(this.dataStoreObjects == null) {
this.dataStoreObjects = DBIDUtil.newHashSet();
}
this.dataStoreObjects.add(object);
currentDataStoreEventType = type;
if(!accumulateDataStoreEvents) {
flushDataStoreEvents();
}
}
/**
* Informs all registered ResultListener
that a new result was
* added.
*
* @param r New child result added
* @param parent Parent result that was added to
*/
public void fireResultAdded(Result r, Result parent) {
for(int i = resultListenerList.size(); --i >= 0;) {
resultListenerList.get(i).resultAdded(r, parent);
}
}
/**
* Informs all registered ResultListener
that a new result has
* been removed.
*
* @param r result that has been removed
* @param parent Parent result that has been removed
*/
public void fireResultRemoved(Result r, Result parent) {
for(int i = resultListenerList.size(); --i >= 0;) {
resultListenerList.get(i).resultRemoved(r, parent);
}
}
}