blob: 6870ff4fcce64438ca1311d2115bddbb3eee8e9d [file] [log] [blame]
package org.eclipse.emf.henshin.sam.paf;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import java.util.Map;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.henshin.sam.paf.annotation.ResultDictEntry;
/**
* Most of the {@link IFilter} instances getting developed only differ in the
* logics of the two methods <code>produce</code> and <code>consume</code>.
* The other methods are not completely but highly redundant. The introduction
* of this skeleton aims at reducing the redundancy. In most cases it is
* appropriate to use this abstract class as super type for your new
* <code>IFilter</code> instance but if in doubt use
* <code>AbstractFilter</code> instead.<br/> The difference between the
* <code>FilterSkeleton</code> and the <code>AbstractFilter</code> is the
* fact that the <code>AbstractFilter</code> class only provides support for
* the association management but the <code>FilterSkeleton</code> in addition
* provides almost complete <code>IFilter</code> implementation.
*
* @author Basil.Becker
*
* @param <T>
* see <code>AbstractFilter</code> for details.
* @param <S>
* see <code>AbstractFilter</code> for details.
*/
public abstract class FilterSkeleton<T, S> extends AbstractFilter<T, S> {
/**
* the <code>IPipe</code> instance normally input is read from
*/
protected IPipe<T> defaultInputPipe;
/**
* the <code>IPipe</code> instance computation result are written to by
* default.
*/
protected IPipe<S> defaultOutputPipe;
protected T currentInput;
private Map<String, Boolean> options;
protected void println(String s) {
this.getFilterDispatcher().outputStream.println(s);
}
protected Boolean getOption(String name) throws RuntimeException{
Boolean result = this.options.get(name);
if (result == null) {
RuntimeException e = new UnknownOptionException("Filter " + this.filterName + " does not register an option '" + name + "'");
PAFActivator.getDefault().getLog().log(new Status(IStatus.ERROR, PAFActivator.PLUGIN_ID, "Configuration option '" + name + "' does not exist for filter '" + this.filterName + "'.", e)); //$NON-NLS-1$
throw e;
} else {
return result;
}
}
/**
* a boolean flag to control whether this <code>IFilter</code> is active.
*/
protected boolean running;
/**
* Initializes the filter.<br/> This method by default initializes the fields
* {@link #defaultInputPipe} and {@link #defaultOutputPipe} with the
* <code>IPipe</code> instances registered under the keys
* {@link AbstractFilter#DEFAULT_INPUT} respective
* {@link AbstractFilter#DEFAULT_OUTPUT}. Does your <code>IFilter</code>
* need a special initialization override this method. This method is
* invoked by the {@link IFilter#filter()} method as the first step.
*/
protected void initFilter() {
this.defaultInputPipe = this
.getInputPipes().get(AbstractFilter.DEFAULT_INPUT);
this.defaultOutputPipe = this
.getOutputPipes().get(AbstractFilter.DEFAULT_OUTPUT);
initData();
this.filterName = this.getClass().getSimpleName();
this.running = this.getFilterDispatcher().isContinueComputation();
}
/**
* Shuts down this filter.<br/> This method's default behavior is to
* decrement the {@link #defaultInputPipe}'s number of readers and to close
* the {@link #defaultOutputPipe}. In case that your <code>IFilter</code>
* needs any special operations to shut down properly simply override this
* method. References to the default input- and output-pipe hold by this <code>IFilter</code> are set to <code>null</code> by this method. If a subclass uses more than the default pipes it has to ensure to unset the references to those by itself. This method is invoked within the {@link #filter()} method's
* finally block.
*/
protected void shutDown() {
if (this.defaultInputPipe != null) {
this.defaultInputPipe.decrementReaders();
this.defaultInputPipe = null;
}
if (this.defaultOutputPipe != null) {
this.defaultOutputPipe.close();
this.defaultOutputPipe = null;
}
}
/**
* This method contains the main computation loop for this
* <code>IFilter</code>.<br/> It is highly recommended not to override
* this method despite you really know what your are doing. The recommended
* way of adapting this skeleton class to your needs is to override
* {@link #initFilter()}, {@link #shutDown()} and {@link #consume()}.
*/
@Override
public void filter() {
this.options = this.getFilterDispatcher().getOptionsFor(this.getClass());
this.initFilter();
try {
while (running) {
this.consume();
if (!running)
continue;
this.produce();
this.running = this.getFilterDispatcher()
.isContinueComputation();
}
} finally {
final ThreadMXBean tmxBean = ManagementFactory.getThreadMXBean();
if (tmxBean != null && tmxBean.isThreadCpuTimeSupported()) {
this.threadTime = tmxBean.getCurrentThreadUserTime();
}
this.shutDown();
}
}
@Override
public void reset() {
// TODO Auto-generated method stub
}
protected void initData() {
}
/**
* A default implementation for {@link IConsumer#consume()}.<br/>This
* method reads the next element from the {@link #defaultInputPipe} and
* stores it to {@link #currentInput}. If a {@link IllegalStateException}
* occurs {@link #running} will be set to <code>false</code>.
*/
public void consume() {
try {
this.currentInput = this.defaultInputPipe.dequeue();
} catch (IllegalStateException ise) {
this.running = false;
} catch (InterruptedException ie) {
this.running = false;
}
}
@Override
protected void prepareResult() {
this.resultDictionary = new LinkedHashMap<String, String>();
final Class<?> clazz = getClass();
for (Field f : clazz.getDeclaredFields()) {
ResultDictEntry rde = f.getAnnotation(ResultDictEntry.class);
if (rde != null) {
Object val = null;
boolean exception = false;
try {
if (f.isAccessible() == false)
f.setAccessible(true);
val = f.get(this);
} catch (IllegalArgumentException e) {
exception = true;
} catch (IllegalAccessException e) {
exception = true;
}
if (exception == false) {
if (val == null) {
val = "Property not set! (null)";
}
this.resultDictionary.put(rde.entryName(), val.toString());
}
}
}
for (Map.Entry<String, Boolean> entry : this.options.entrySet()) {
this.resultDictionary.put(entry.getKey(), entry.getValue().toString());
}
}
}