/**
* 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.job.builder;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eobjects.analyzer.data.InputColumn;
import org.eobjects.analyzer.data.MetaModelInputColumn;
import org.eobjects.analyzer.descriptors.ConfiguredPropertyDescriptor;
import org.eobjects.analyzer.job.AnalysisJob;
import org.eobjects.analyzer.job.AnyComponentRequirement;
import org.eobjects.analyzer.job.ComponentJob;
import org.eobjects.analyzer.job.ComponentRequirement;
import org.eobjects.analyzer.job.CompoundComponentRequirement;
import org.eobjects.analyzer.job.ConfigurableBeanJob;
import org.eobjects.analyzer.job.FilterOutcome;
import org.eobjects.analyzer.job.HasFilterOutcomes;
import org.eobjects.analyzer.job.InputColumnSourceJob;
import org.eobjects.analyzer.job.SimpleComponentRequirement;
import org.eobjects.analyzer.util.SourceColumnFinder;
import org.apache.metamodel.schema.Column;
/**
* Helper class to perform the somewhat intricate
* {@link AnalysisJobBuilder#importJob(AnalysisJob)} operation.
*/
final class AnalysisJobBuilderImportHelper {
private final AnalysisJobBuilder _builder;
public AnalysisJobBuilderImportHelper(AnalysisJobBuilder builder) {
_builder = builder;
}
public void importJob(AnalysisJob job) {
_builder.setDatastore(job.getDatastore());
final Collection<InputColumn<?>> sourceColumns = job.getSourceColumns();
for (InputColumn<?> inputColumn : sourceColumns) {
_builder.addSourceColumn((MetaModelInputColumn) inputColumn);
}
final SourceColumnFinder sourceColumnFinder = new SourceColumnFinder();
sourceColumnFinder.addSources(job);
// map that translates original component jobs to their builder objects
final Map<ComponentJob, Object> componentBuilders = new IdentityHashMap<ComponentJob, Object>();
addComponentBuilders(job.getFilterJobs(), componentBuilders);
addComponentBuilders(job.getTransformerJobs(), componentBuilders);
addComponentBuilders(job.getAnalyzerJobs(), componentBuilders);
// re-build filter requirements
for (Entry<ComponentJob, Object> entry : componentBuilders.entrySet()) {
ComponentJob componentJob = entry.getKey();
if (componentJob instanceof ConfigurableBeanJob<?>) {
final ComponentRequirement originalRequirement = componentJob.getComponentRequirement();
final ComponentRequirement componentRequirement = findImportedRequirement(originalRequirement,
componentBuilders);
final AbstractBeanWithInputColumnsBuilder<?, ?, ?> builder = (AbstractBeanWithInputColumnsBuilder<?, ?, ?>) entry
.getValue();
builder.setComponentRequirement(componentRequirement);
}
}
// re-build input column dependencies
for (Entry<ComponentJob, Object> entry : componentBuilders.entrySet()) {
final ComponentJob componentJob = entry.getKey();
if (componentJob instanceof ConfigurableBeanJob) {
final ConfigurableBeanJob<?> configurableBeanJob = (ConfigurableBeanJob<?>) componentJob;
final Set<ConfiguredPropertyDescriptor> inputColumnProperties = configurableBeanJob.getDescriptor()
.getConfiguredPropertiesForInput(true);
final AbstractBeanWithInputColumnsBuilder<?, ?, ?> builder = (AbstractBeanWithInputColumnsBuilder<?, ?, ?>) entry
.getValue();
for (ConfiguredPropertyDescriptor inputColumnProperty : inputColumnProperties) {
final Object originalInputColumnValue = configurableBeanJob.getConfiguration().getProperty(
inputColumnProperty);
final Object newInputColumnValue = findImportedInputColumns(originalInputColumnValue,
componentBuilders, sourceColumnFinder);
builder.setConfiguredProperty(inputColumnProperty, newInputColumnValue);
}
}
}
}
private Object findImportedInputColumns(Object originalInputColumnValue,
Map<ComponentJob, Object> componentBuilders, SourceColumnFinder sourceColumnFinder) {
if (originalInputColumnValue == null) {
return null;
}
if (originalInputColumnValue instanceof InputColumn) {
return findImportedInputColumn((InputColumn<?>) originalInputColumnValue, componentBuilders,
sourceColumnFinder);
}
if (originalInputColumnValue.getClass().isArray()) {
int length = Array.getLength(originalInputColumnValue);
InputColumn<?>[] value = new InputColumn[length];
for (int i = 0; i < value.length; i++) {
InputColumn<?> element = (InputColumn<?>) Array.get(originalInputColumnValue, i);
value[i] = findImportedInputColumn(element, componentBuilders, sourceColumnFinder);
}
return value;
}
throw new UnsupportedOperationException("Unknown input column value type: " + originalInputColumnValue);
}
private InputColumn<?> findImportedInputColumn(InputColumn<?> originalInputColumn,
Map<ComponentJob, Object> componentBuilders, SourceColumnFinder sourceColumnFinder) {
if (originalInputColumn.isPhysicalColumn()) {
Column physicalColumn = originalInputColumn.getPhysicalColumn();
return _builder.getSourceColumnByName(physicalColumn.getQualifiedLabel());
}
final InputColumnSourceJob originalSourceJob = sourceColumnFinder.findInputColumnSource(originalInputColumn);
final InputColumnSourceJob newSourceJob = (InputColumnSourceJob) componentBuilders.get(originalSourceJob);
if (newSourceJob == null) {
throw new IllegalStateException("Could not find builder corresponding to " + originalSourceJob
+ " in builder map: " + componentBuilders);
}
final String originalColumnName = originalInputColumn.getName();
final InputColumn<?>[] candidates = newSourceJob.getOutput();
for (InputColumn<?> candidate : candidates) {
if (candidate.getName().equals(originalColumnName)) {
return candidate;
}
}
throw new IllegalStateException("Could not determine a replacement input column for '" + originalColumnName
+ "' in output column candidate set: " + Arrays.toString(candidates));
}
private ComponentRequirement findImportedRequirement(ComponentRequirement originalRequirement,
Map<ComponentJob, Object> componentBuilders) {
if (originalRequirement == null) {
return null;
}
if (originalRequirement instanceof AnyComponentRequirement) {
return AnyComponentRequirement.get();
}
if (originalRequirement instanceof SimpleComponentRequirement) {
final FilterOutcome originalFilterOutcome = ((SimpleComponentRequirement) originalRequirement).getOutcome();
final FilterOutcome newOutcome = findFilterOutcome(originalFilterOutcome, componentBuilders);
return new SimpleComponentRequirement(newOutcome);
}
if (originalRequirement instanceof CompoundComponentRequirement) {
final Set<FilterOutcome> originalOutcomes = ((CompoundComponentRequirement) originalRequirement)
.getOutcomes();
final Collection<FilterOutcome> newOutcomes = new HashSet<>();
for (final FilterOutcome originalOutcome : originalOutcomes) {
FilterOutcome newOutcome = findFilterOutcome(originalOutcome, componentBuilders);
newOutcomes.add(newOutcome);
}
return new CompoundComponentRequirement(newOutcomes);
}
throw new UnsupportedOperationException("Unsupported requirement type: " + originalRequirement);
}
private FilterOutcome findFilterOutcome(FilterOutcome originalFilterOutcome,
Map<ComponentJob, Object> componentBuilders) {
final HasFilterOutcomes source = originalFilterOutcome.getSource();
final Object builder = componentBuilders.get(source);
if (builder == null) {
throw new IllegalStateException("Could not find builder corresponding to " + source + " in builder map: "
+ componentBuilders);
}
final Enum<?> category = originalFilterOutcome.getCategory();
final FilterJobBuilder<?, ?> filterJobBuilder = (FilterJobBuilder<?, ?>) builder;
final FilterOutcome newOutcome = filterJobBuilder.getFilterOutcome(category);
return newOutcome;
}
private void addComponentBuilders(Collection<? extends ComponentJob> componentJobs,
Map<ComponentJob, Object> componentBuilders) {
for (ComponentJob componentJob : componentJobs) {
Object builder = _builder.addComponent(componentJob);
componentBuilders.put(componentJob, builder);
}
}
}