/*
* Copyright (c) 2010 StockPlay development team
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kapti.filter;
import com.kapti.exceptions.FilterException;
import com.kapti.exceptions.StockPlayException;
import com.kapti.filter.graph.Graph;
import com.kapti.filter.condition.Condition;
import com.kapti.filter.relation.Relation;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
/**
* Dit is de container voor een filterobject. Dit object bevat eigenlijk
* gewoon een root-conditie (de top van de filterboom), en voegt daar
* wat functionaliteit aan toe, zoals het initiƫren van een compilatie,
* of het uitschrijven van een debug-dump.
*
* @author tim
*/
public class Filter implements Serializable {
//
// Member data
//
static Logger mLogger = Logger.getLogger(Filter.class);
private Condition mRoot = null;
//
// Constructie
//
public Filter() {
}
public Filter(Condition iRoot) {
mRoot = iRoot;
}
//
// Methods
//
/**
* Stel de root conditie in. Dit is de top van de filter tree, en zal dus
* het startpunt zijn voor verschillende acties (compilatie, debug).
*
* @param iRoot
*/
public final void setRoot(Condition iRoot) {
mRoot = iRoot;
}
/**
* Compileer de filter naar een bruikbaar formaat.
*
* @return
* @throws FilterException
*/
public Object compile(String iConverterClass) throws FilterException {
Convertable.setConverterClass(iConverterClass);
if (empty())
return null;
Object oResult = mRoot.compile();
mLogger.debug("compiled filter to '" + oResult + "'");
return oResult;
}
/**
* Schrijf een debug-graph van de filterboom naar een (png) bestand.
*
* @param iFilename
* @throws FilterException
*/
public void debug(String iFilename) throws StockPlayException {
if (empty())
return;
// Create graph
Graph graph = new Graph("digraph");
mRoot.addNode(graph);
// Render panel to file
File tFile = null;
try {
tFile = new File(iFilename + ".dot");
if (!tFile.exists())
tFile.createNewFile();
graph.render(new PrintStream(tFile));
}
catch (IOException e) {
throw new FilterException(FilterException.Type.DEBUG_FAILURE, "Problem creating output file", e.getCause());
}
// Render the DOT file to a PNG
String[] cmd = { "/usr/bin/dot", "-Tpng", iFilename+".dot", "-o", iFilename+".png" };
try {
Process tConverter = Runtime.getRuntime().exec(cmd);
tConverter.waitFor();
if (tConverter.exitValue() != 0)
throw new FilterException(FilterException.Type.DEBUG_FAILURE, "Conversion failed");
}
catch (IOException e) {
throw new FilterException(FilterException.Type.DEBUG_FAILURE, "Problem executing child process to convert DOT code", e.getCause());
}
catch (InterruptedException e) {
throw new FilterException(FilterException.Type.DEBUG_FAILURE, "Was not allowed to wait for DOT output due to interrupt", e.getCause());
}
}
/**
* Controleer of de boom gedefiniƫerd is.
*
* @return
*/
public boolean empty() {
return mRoot == null;
}
/**
* Voeg twee bomen samen.
*
* @return
*/
public static Filter merge(Class<? extends Relation> iRelation, Filter... iFilters) throws FilterException {
try {
Constructor<? extends Condition> c = iRelation.getConstructor(List.class);
Filter oFilter = new Filter();
// Fetch all the root nodex
List<Condition> tNodes = new ArrayList<Condition>();
for (Filter tFilter : iFilters) {
if (! tFilter.empty())
tNodes.add(tFilter.mRoot);
}
// Merge the root nodes using the given relation
switch (tNodes.size()) {
case 0:
break;
case 1:
oFilter.mRoot = tNodes.get(0);
break;
default:
oFilter.mRoot = c.newInstance(tNodes);
}
return oFilter;
}
catch (Exception e) {
throw new FilterException(FilterException.Type.MERGE_FAILURE, "construction of relation failed", e.getCause());
}
}
@Override
public int hashCode() {
int code = 1;
if (!empty()) {
code = (31 * code) + mRoot.hashCode();
}
return code;
}
@Override
public boolean equals(Object o) {
// Compare memory address
if (this == o) {
return true;
}
// Check if types match
if (!(o instanceof Filter)) {
return false;
}
// Check the hashcode
final Filter filter = (Filter) o;
if (filter.hashCode() != this.hashCode())
return false;
return true;
}
@Override
public String toString() {
try {
return ((String)this.compile("filter"));
} catch (FilterException e) {
return e.getMessage();
}
}
}