package org.simpleflatmapper.map.mapper;
import org.simpleflatmapper.converter.UncheckedConverter;
import org.simpleflatmapper.map.Mapper;
import org.simpleflatmapper.map.MappingContext;
import org.simpleflatmapper.map.MappingException;
import org.simpleflatmapper.util.Enumarable;
import org.simpleflatmapper.util.ErrorHelper;
import org.simpleflatmapper.util.Predicate;
public class DiscriminatorEnumerable<S, T> implements Enumarable<T> {
private final PredicatedMapperWithContext<S, T>[] discriminatorMappers;
private final Enumarable<S> sourceEnumarable;
private final UncheckedConverter<? super S, ? extends CharSequence> errorMessageGenerator;
private T currentValue;
private T nextValue;
private Mapper<S, T> currentMapper;
private MappingContext<? super S> currentMappingContext;
public DiscriminatorEnumerable(
PredicatedMapperWithContext<S, T>[] discriminatorMappers,
Enumarable<S> sourceEnumarable,
UncheckedConverter<? super S, ? extends CharSequence> errorMessageGenerator) {
this.discriminatorMappers = discriminatorMappers;
this.sourceEnumarable = sourceEnumarable;
this.errorMessageGenerator = errorMessageGenerator;
}
@Override
public boolean next() {
try {
currentValue = nextValue;
nextValue = null;
while (sourceEnumarable.next()) {
checkMapper();
S source = sourceEnumarable.currentValue();
if (currentMappingContext.broke(source)) {
if (currentValue == null) {
currentValue = currentMapper.map(source, currentMappingContext);
} else {
nextValue = currentMapper.map(source, currentMappingContext);
return true;
}
} else {
currentMapper.mapTo(source, currentValue, currentMappingContext);
}
}
return currentValue != null;
} catch (Exception e) {
ErrorHelper.rethrow(e);
return false;
}
}
public T currentValue() {
return currentValue;
}
private void checkMapper() {
for(PredicatedMapperWithContext<S, T> pmm : discriminatorMappers ) {
if (pmm.predicate.test(sourceEnumarable.currentValue())) {
if (pmm.mapper != currentMapper) {
markAsBroken();
currentMapper = pmm.mapper;
currentMappingContext = pmm.mappingContext;
}
return;
}
}
mapperNotFound();
}
private void mapperNotFound() {
CharSequence errorMessage = errorMessageGenerator.convert(sourceEnumarable.currentValue());
throw new MappingException("No mapper found for " + errorMessage);
}
private void markAsBroken() {
for(PredicatedMapperWithContext<S, T> pmm : discriminatorMappers ) {
pmm.mappingContext.markAsBroken();
}
}
public static class PredicatedMapperWithContext<ROW, T> {
private final Predicate<ROW> predicate;
private final Mapper<ROW, T> mapper;
private final MappingContext<? super ROW> mappingContext;
public PredicatedMapperWithContext(Predicate<ROW> predicate, Mapper<ROW, T> mapper, MappingContext<? super ROW> mappingContext) {
this.predicate = predicate;
this.mapper = mapper;
this.mappingContext = mappingContext;
}
}
}