/**
* AnalyzerBeans
* Copyright (C) 2014 Neopost - Customer Information Management
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.eobjects.analyzer.test.full.scenarios;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import junit.framework.TestCase;
import org.apache.metamodel.pojo.ArrayTableDataProvider;
import org.apache.metamodel.util.SimpleTableDef;
import org.eobjects.analyzer.beans.api.Configured;
import org.eobjects.analyzer.beans.api.Filter;
import org.eobjects.analyzer.beans.api.FilterBean;
import org.eobjects.analyzer.configuration.AnalyzerBeansConfiguration;
import org.eobjects.analyzer.configuration.AnalyzerBeansConfigurationImpl;
import org.eobjects.analyzer.connection.DatastoreCatalog;
import org.eobjects.analyzer.connection.DatastoreCatalogImpl;
import org.eobjects.analyzer.connection.PojoDatastore;
import org.eobjects.analyzer.data.InputColumn;
import org.eobjects.analyzer.data.InputRow;
import org.eobjects.analyzer.data.MutableInputColumn;
import org.eobjects.analyzer.job.AnalysisJob;
import org.eobjects.analyzer.job.AnyComponentRequirement;
import org.eobjects.analyzer.job.CompoundComponentRequirement;
import org.eobjects.analyzer.job.FilterOutcome;
import org.eobjects.analyzer.job.builder.AnalysisJobBuilder;
import org.eobjects.analyzer.job.builder.AnalyzerJobBuilder;
import org.eobjects.analyzer.job.builder.FilterJobBuilder;
import org.eobjects.analyzer.job.builder.TransformerJobBuilder;
import org.eobjects.analyzer.job.concurrent.SingleThreadedTaskRunner;
import org.eobjects.analyzer.job.runner.AnalysisResultFuture;
import org.eobjects.analyzer.job.runner.AnalysisRunnerImpl;
import org.eobjects.analyzer.result.ListResult;
import org.eobjects.analyzer.test.MockAnalyzer;
import org.eobjects.analyzer.test.MockTransformer;
public class FilterRequirementMergingTest extends TestCase {
private AnalyzerBeansConfiguration configuration;
private PojoDatastore datastore;
private AnalysisJobBuilder jobBuilder;
@Override
protected void setUp() throws Exception {
super.setUp();
SimpleTableDef tableDef = new SimpleTableDef("table", new String[] { "col1" });
List<Object[]> rowData = new ArrayList<Object[]>();
rowData.add(new Object[] { "foo" });
rowData.add(new Object[] { "bar" });
rowData.add(new Object[] { "baz" });
rowData.add(new Object[] { "hello" });
rowData.add(new Object[] { "world" });
datastore = new PojoDatastore("ds", "sch", new ArrayTableDataProvider(tableDef, rowData));
DatastoreCatalog datastoreCatalog = new DatastoreCatalogImpl(datastore);
configuration = new AnalyzerBeansConfigurationImpl().replace(datastoreCatalog).replace(
new SingleThreadedTaskRunner());
jobBuilder = new AnalysisJobBuilder(configuration);
jobBuilder.setDatastore(datastore);
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
jobBuilder.close();
}
/**
* Tests that if two transformations that have different (opposing)
* requirements both feed into the same component (e.g. an Analyzer), then
* both requirement states will be accepted by that component.
*/
public void testMergeFilterRequirementsWhenAnalyzerConsumesInputColumnsWithMultipleRequirements() throws Throwable {
jobBuilder.addSourceColumns("col1");
InputColumn<?> sourceColumn = jobBuilder.getSourceColumnByName("col1");
FilterJobBuilder<EvenOddFilter, EvenOddFilter.Category> filter = jobBuilder.addFilter(EvenOddFilter.class)
.addInputColumn(sourceColumn);
FilterOutcome req1 = filter.getFilterOutcome(EvenOddFilter.Category.EVEN);
FilterOutcome req2 = filter.getFilterOutcome(EvenOddFilter.Category.ODD);
TransformerJobBuilder<MockTransformer> transformer1 = jobBuilder.addTransformer(MockTransformer.class)
.addInputColumn(sourceColumn);
transformer1.setRequirement(req1);
MutableInputColumn<?> outputColumn1 = transformer1.getOutputColumns().get(0);
outputColumn1.setName("outputColumn1");
TransformerJobBuilder<MockTransformer> transformer2 = jobBuilder.addTransformer(MockTransformer.class)
.addInputColumn(sourceColumn);
transformer2.setRequirement(req2);
MutableInputColumn<?> outputColumn2 = transformer2.getOutputColumns().get(0);
outputColumn2.setName("outputColumn2");
AnalyzerJobBuilder<MockAnalyzer> analyzer = jobBuilder.addAnalyzer(MockAnalyzer.class);
// add outputcolumn 1+2 - they have opposite requirements
analyzer.addInputColumns(sourceColumn, outputColumn1, outputColumn2);
AnalysisJob job = jobBuilder.toAnalysisJob();
AnalysisResultFuture resultFuture = new AnalysisRunnerImpl(configuration).run(job);
resultFuture.await();
if (resultFuture.isErrornous()) {
throw resultFuture.getErrors().get(0);
}
@SuppressWarnings("unchecked")
ListResult<InputRow> listResult = (ListResult<InputRow>) resultFuture.getResults().get(0);
List<InputRow> list = listResult.getValues();
assertFalse("List is empty - this indicates that no records passed through the 'multiple requirements' rule",
list.isEmpty());
assertEquals("[foo, null, mocked: foo]", list.get(0).getValues(sourceColumn, outputColumn1, outputColumn2)
.toString());
assertEquals("[bar, mocked: bar, null]", list.get(1).getValues(sourceColumn, outputColumn1, outputColumn2)
.toString());
assertEquals("[baz, null, mocked: baz]", list.get(2).getValues(sourceColumn, outputColumn1, outputColumn2)
.toString());
assertEquals("[hello, mocked: hello, null]", list.get(3).getValues(sourceColumn, outputColumn1, outputColumn2)
.toString());
assertEquals("[world, null, mocked: world]", list.get(4).getValues(sourceColumn, outputColumn1, outputColumn2)
.toString());
assertEquals(5, list.size());
}
/**
* Tests that if a single transformations that has a requirements feed into
* the another component (e.g. an Analyzer), then that component will
* respect the transformation's requirement.
*/
public void testDontMergeFilterRequirementWhenAnalyzerConsumesInputColumnsWithSingleRequirement() throws Throwable {
jobBuilder.addSourceColumns("col1");
InputColumn<?> sourceColumn = jobBuilder.getSourceColumnByName("col1");
FilterJobBuilder<EvenOddFilter, EvenOddFilter.Category> filter = jobBuilder.addFilter(EvenOddFilter.class)
.addInputColumn(sourceColumn);
FilterOutcome req1 = filter.getFilterOutcome(EvenOddFilter.Category.EVEN);
FilterOutcome req2 = filter.getFilterOutcome(EvenOddFilter.Category.ODD);
TransformerJobBuilder<MockTransformer> transformer1 = jobBuilder.addTransformer(MockTransformer.class)
.addInputColumn(sourceColumn);
transformer1.setRequirement(req1);
MutableInputColumn<?> outputColumn1 = transformer1.getOutputColumns().get(0);
outputColumn1.setName("outputColumn1");
TransformerJobBuilder<MockTransformer> transformer2 = jobBuilder.addTransformer(MockTransformer.class)
.addInputColumn(sourceColumn);
transformer2.setRequirement(req2);
MutableInputColumn<?> outputColumn2 = transformer2.getOutputColumns().get(0);
outputColumn2.setName("outputColumn2");
AnalyzerJobBuilder<MockAnalyzer> analyzer = jobBuilder.addAnalyzer(MockAnalyzer.class);
// add only outputcolumn 1 - it has a single requirement
analyzer.addInputColumns(sourceColumn, outputColumn1);
AnalysisJob job = jobBuilder.toAnalysisJob();
AnalysisResultFuture resultFuture = new AnalysisRunnerImpl(configuration).run(job);
resultFuture.await();
if (resultFuture.isErrornous()) {
throw resultFuture.getErrors().get(0);
}
@SuppressWarnings("unchecked")
ListResult<InputRow> listResult = (ListResult<InputRow>) resultFuture.getResults().get(0);
List<InputRow> list = listResult.getValues();
assertFalse("List is empty - this indicates that no records passed through the 'single requirements' rule",
list.isEmpty());
assertEquals("[bar, mocked: bar, null]", list.get(0).getValues(sourceColumn, outputColumn1, outputColumn2)
.toString());
assertEquals("[hello, mocked: hello, null]", list.get(1).getValues(sourceColumn, outputColumn1, outputColumn2)
.toString());
assertEquals(2, list.size());
}
/**
* Tests the use of the '_any_' requirement. This should override the
* transitive requirement behaviour
*/
public void testConsumeRecordsWhenAnyOutcomeRequirementIsSet() throws Throwable {
jobBuilder.addSourceColumns("col1");
InputColumn<?> sourceColumn = jobBuilder.getSourceColumnByName("col1");
FilterJobBuilder<EvenOddFilter, EvenOddFilter.Category> filter = jobBuilder.addFilter(EvenOddFilter.class)
.addInputColumn(sourceColumn);
FilterOutcome req1 = filter.getFilterOutcome(EvenOddFilter.Category.EVEN);
FilterOutcome req2 = filter.getFilterOutcome(EvenOddFilter.Category.ODD);
TransformerJobBuilder<MockTransformer> transformer1 = jobBuilder.addTransformer(MockTransformer.class)
.addInputColumn(sourceColumn);
transformer1.setRequirement(req1);
MutableInputColumn<?> outputColumn1 = transformer1.getOutputColumns().get(0);
outputColumn1.setName("outputColumn1");
TransformerJobBuilder<MockTransformer> transformer2 = jobBuilder.addTransformer(MockTransformer.class)
.addInputColumn(sourceColumn);
transformer2.setRequirement(req2);
MutableInputColumn<?> outputColumn2 = transformer2.getOutputColumns().get(0);
outputColumn2.setName("outputColumn2");
AnalyzerJobBuilder<MockAnalyzer> analyzer = jobBuilder.addAnalyzer(MockAnalyzer.class);
// add only outputcolumn 1 - it has a single requirement
analyzer.addInputColumns(sourceColumn, outputColumn1);
analyzer.setComponentRequirement(AnyComponentRequirement.get());
AnalysisJob job = jobBuilder.toAnalysisJob();
AnalysisResultFuture resultFuture = new AnalysisRunnerImpl(configuration).run(job);
resultFuture.await();
if (resultFuture.isErrornous()) {
throw resultFuture.getErrors().get(0);
}
@SuppressWarnings("unchecked")
ListResult<InputRow> listResult = (ListResult<InputRow>) resultFuture.getResults().get(0);
List<InputRow> list = listResult.getValues();
assertFalse("List is empty - this indicates that no records passed through the 'any requirements' rule",
list.isEmpty());
assertEquals("[foo, null, mocked: foo]", list.get(0).getValues(sourceColumn, outputColumn1, outputColumn2)
.toString());
assertEquals("[bar, mocked: bar, null]", list.get(1).getValues(sourceColumn, outputColumn1, outputColumn2)
.toString());
assertEquals("[baz, null, mocked: baz]", list.get(2).getValues(sourceColumn, outputColumn1, outputColumn2)
.toString());
assertEquals("[hello, mocked: hello, null]", list.get(3).getValues(sourceColumn, outputColumn1, outputColumn2)
.toString());
assertEquals("[world, null, mocked: world]", list.get(4).getValues(sourceColumn, outputColumn1, outputColumn2)
.toString());
assertEquals(5, list.size());
}
/**
* Tests the use of the '_any_' requirement. This should override the
* transitive requirement behaviour
*/
public void testConsumeRecordsWhenCompoundOutcomeRequirementIsSet() throws Throwable {
jobBuilder.addSourceColumns("col1");
InputColumn<?> sourceColumn = jobBuilder.getSourceColumnByName("col1");
FilterJobBuilder<EvenOddFilter, EvenOddFilter.Category> filter = jobBuilder.addFilter(EvenOddFilter.class)
.addInputColumn(sourceColumn);
FilterOutcome req1 = filter.getFilterOutcome(EvenOddFilter.Category.EVEN);
FilterOutcome req2 = filter.getFilterOutcome(EvenOddFilter.Category.ODD);
TransformerJobBuilder<MockTransformer> transformer1 = jobBuilder.addTransformer(MockTransformer.class)
.addInputColumn(sourceColumn);
transformer1.setRequirement(req1);
MutableInputColumn<?> outputColumn1 = transformer1.getOutputColumns().get(0);
outputColumn1.setName("outputColumn1");
TransformerJobBuilder<MockTransformer> transformer2 = jobBuilder.addTransformer(MockTransformer.class)
.addInputColumn(sourceColumn);
transformer2.setRequirement(req2);
MutableInputColumn<?> outputColumn2 = transformer2.getOutputColumns().get(0);
outputColumn2.setName("outputColumn2");
AnalyzerJobBuilder<MockAnalyzer> analyzer = jobBuilder.addAnalyzer(MockAnalyzer.class);
// add only outputcolumn 1 - it has a single requirement
analyzer.addInputColumns(sourceColumn, outputColumn1);
analyzer.setComponentRequirement(new CompoundComponentRequirement(req1, req2));
AnalysisJob job = jobBuilder.toAnalysisJob();
AnalysisResultFuture resultFuture = new AnalysisRunnerImpl(configuration).run(job);
resultFuture.await();
if (resultFuture.isErrornous()) {
throw resultFuture.getErrors().get(0);
}
@SuppressWarnings("unchecked")
ListResult<InputRow> listResult = (ListResult<InputRow>) resultFuture.getResults().get(0);
List<InputRow> list = listResult.getValues();
assertFalse("List is empty - this indicates that no records passed through the 'any requirements' rule",
list.isEmpty());
assertEquals("[foo, null, mocked: foo]", list.get(0).getValues(sourceColumn, outputColumn1, outputColumn2)
.toString());
assertEquals("[bar, mocked: bar, null]", list.get(1).getValues(sourceColumn, outputColumn1, outputColumn2)
.toString());
assertEquals("[baz, null, mocked: baz]", list.get(2).getValues(sourceColumn, outputColumn1, outputColumn2)
.toString());
assertEquals("[hello, mocked: hello, null]", list.get(3).getValues(sourceColumn, outputColumn1, outputColumn2)
.toString());
assertEquals("[world, null, mocked: world]", list.get(4).getValues(sourceColumn, outputColumn1, outputColumn2)
.toString());
assertEquals(5, list.size());
}
@FilterBean("Even/odd record filter")
public static class EvenOddFilter implements Filter<EvenOddFilter.Category> {
public static enum Category {
EVEN, ODD
}
private final AtomicInteger counter = new AtomicInteger();
@Configured
InputColumn<String> column;
@Override
public Category categorize(InputRow inputRow) {
int v = counter.incrementAndGet();
if (v % 2 == 0) {
return Category.EVEN;
}
return Category.ODD;
}
}
}