package edu.umd.rhsmith.diads.meater.core.app.components;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import edu.umd.rhsmith.diads.meater.core.app.MEaterConfigurationException;
import edu.umd.rhsmith.diads.meater.core.app.components.media.MediaManager;
import edu.umd.rhsmith.diads.meater.core.app.components.media.MediaProcessor;
import edu.umd.rhsmith.diads.meater.core.app.components.media.MediaSource;
import edu.umd.rhsmith.diads.meater.util.ControlUnit;
public abstract class Component extends ControlUnit {
private final String name;
private ComponentManager componentManager;
private final Object initWaiter;
protected final Object initLock;
private boolean initBegun;
private boolean initFinished;
private final Set<MediaSource<?>> mediaSources;
private final Set<MediaProcessor<?>> mediaProcessors;
public Component(ComponentInitializer init)
throws MEaterConfigurationException {
super();
this.name = init.getInstanceName();
this.setLogName(this.name);
this.initWaiter = new Object();
this.initLock = new Object();
this.initBegun = false;
this.initFinished = false;
this.mediaSources = new HashSet<MediaSource<?>>();
this.mediaProcessors = new HashSet<MediaProcessor<?>>();
this.setupExpectedMedia(init);
}
/*
* --------------------------------
* General getters/setters
* --------------------------------
*/
public final String getName() {
return this.name;
}
public ComponentManager getComponentManager() {
return componentManager;
}
public MediaManager getMediaManager() {
if (this.componentManager == null) {
return null;
} else {
return this.componentManager.getMediaManager();
}
}
final void setComponentManager(ComponentManager mgr)
throws IllegalStateException {
synchronized (this.controlLock) {
this.requireUnStarted();
this.componentManager = mgr;
this.setLogger(mgr.getLogger());
}
}
/*
* --------------------------------
* Control methods
* --------------------------------
*/
public final void initialize() throws MEaterConfigurationException {
synchronized (this.initLock) {
this.requireUnInited();
synchronized (this.initWaiter) {
this.initBegun = true;
this.getLogger().info(this.messageString(MSG_INITING));
this.initWaiter.notifyAll();
}
this.doInitRoutine();
// check that we have the media nodes that we expected
this.verifyExpectedMedia();
synchronized (this.initWaiter) {
this.initFinished = true;
this.getLogger().info(this.messageString(MSG_INITED));
this.initWaiter.notifyAll();
}
}
}
protected abstract void doInitRoutine() throws MEaterConfigurationException;
/*
* --------------------------------
* Control status-checkers
* --------------------------------
*/
public final boolean isInitBegun() {
return initBegun;
}
public final boolean isInitFinished() {
return initFinished;
}
/*
* --------------------------------
* Control state-validators
* --------------------------------
*/
public final void requireInited() throws IllegalStateException {
synchronized (this.initLock) {
if (!this.initFinished) {
throw new IllegalStateException(this
.messageString(MSG_ERR_NOSTART));
}
}
}
public final void requireUnInited() throws IllegalStateException {
synchronized (this.initLock) {
if (this.initBegun) {
throw new IllegalStateException(this
.messageString(MSG_ERR_START));
}
}
}
/*
* --------------------------------
* Control status-waiters
* --------------------------------
*/
public final void awaitInitBegun() throws InterruptedException {
synchronized (this.initWaiter) {
while (!this.initBegun) {
this.initWaiter.wait();
}
}
}
public final void awaitInitFinished() throws InterruptedException {
synchronized (this.initWaiter) {
while (!this.initFinished) {
this.initWaiter.wait();
}
}
}
/*
* --------------------------------
* Media interaction
* --------------------------------
*/
public Set<MediaSource<?>> getMediaSources() {
return new HashSet<MediaSource<?>>(this.mediaSources);
}
protected void registerMediaSource(MediaSource<?> source)
throws MEaterConfigurationException {
this.mediaSources.add(source);
}
public Set<MediaProcessor<?>> getMediaProcessors() {
return new HashSet<MediaProcessor<?>>(this.mediaProcessors);
}
protected void registerMediaProcessor(MediaProcessor<?> processor)
throws MEaterConfigurationException {
this.mediaProcessors.add(processor);
}
/*
* --------------------------------
* Media validation
* --------------------------------
*/
private Map<String, Class<?>> expectedSources;
private Map<String, Class<?>> expectedProcessors;
private void setupExpectedMedia(ComponentInitializer init) {
this.expectedProcessors = new HashMap<String, Class<?>>();
this.expectedSources = new HashMap<String, Class<?>>();
}
public final void expectMediaSource(String name, Class<?> mediaClass) {
this.expectedSources.put(name, mediaClass);
}
public final void expectMediaProcessor(String name, Class<?> mediaClass) {
this.expectedProcessors.put(name, mediaClass);
}
private void validateMediaSource(MediaSource<?> source)
throws MEaterConfigurationException {
Class<?> expectedMediaClass = this.expectedSources.remove(source
.getSourceName());
if (expectedMediaClass == null) {
return;
}
if (!expectedMediaClass.isAssignableFrom(source.getMediaClass())) {
throw new IllegalStateException(this.messageString(
MSG_ERR_SRC_INCOMPATIBLE_FMT, source.getSourceName(),
source.getMediaClass(), expectedMediaClass));
}
}
private void validateMediaProcessor(MediaProcessor<?> processor)
throws MEaterConfigurationException {
Class<?> expectedMediaClass = this.expectedProcessors.remove(processor
.getProcessorName());
if (expectedMediaClass == null) {
return;
}
if (!expectedMediaClass.isAssignableFrom(processor.getMediaClass())) {
throw new IllegalStateException(this.messageString(
MSG_ERR_PROC_INCOMPATIBLE_FMT,
processor.getProcessorName(), processor.getMediaClass(),
expectedMediaClass));
}
}
private void verifyExpectedMedia() throws MEaterConfigurationException {
StringBuilder error = new StringBuilder();
boolean missing = false;
for (MediaProcessor<?> processor : this.mediaProcessors) {
this.validateMediaProcessor(processor);
}
for (MediaSource<?> source : this.mediaSources) {
this.validateMediaSource(source);
}
for (String name : this.expectedSources.keySet()) {
error.append(name);
error.append("\n");
missing = true;
}
for (String name : this.expectedProcessors.keySet()) {
error.append(name);
error.append("\n");
missing = true;
}
if (missing) {
throw new MEaterConfigurationException(this.messageString(
MSG_ERR_MISSING_EXPECTED, error.toString()));
}
}
/*
* --------------------------------
* Messages
* --------------------------------
*/
private static final String MSG_ERR_MISSING_EXPECTED = "Expected media sources / processors were not registered --\n%s";
private static final String MSG_ERR_PROC_INCOMPATIBLE_FMT = "Media processor '%s' has media class %s incompatible with declared class %s";
private static final String MSG_ERR_SRC_INCOMPATIBLE_FMT = "Media source '%s' has media class %s incompatible with declared class %s";
private static final String MSG_ERR_NOSTART = "not yet initialized";
private static final String MSG_ERR_START = "already initialized";
private static final String MSG_INITING = "Component initializing";
private static final String MSG_INITED = "Component initializing";
}