
cdc.impex.core.ImportRowImpl Maven / Gradle / Ivy
package cdc.impex.core;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import cdc.impex.ImpExNames;
import cdc.impex.imports.ImportIssueType;
import cdc.impex.imports.ImportIssues;
import cdc.impex.imports.ImportRow;
import cdc.impex.templates.ColumnTemplate;
import cdc.impex.templates.ImportAction;
import cdc.impex.templates.SheetTemplate;
import cdc.impex.templates.SheetTemplateInstance;
import cdc.issues.Issue;
import cdc.issues.IssueSeverity;
import cdc.issues.locations.WorkbookLocation;
import cdc.util.lang.Checks;
public final class ImportRowImpl implements ImportRow {
private final String systemId;
private final SheetTemplateInstance templateInstance;
private final String sheetName;
private final int number;
private final Map rawData;
private final Map data = new HashMap<>();
private List issues = null;
private ImportRowImpl(Builder builder) {
this.systemId = builder.systemId;
this.templateInstance = Checks.isNotNull(builder.templateInstance, "templateInstance");
this.sheetName = builder.sheetName;
this.number = builder.number;
this.rawData = new HashMap<>(builder.rawData);
final SheetTemplate template = templateInstance.getTemplate();
// Don't want to pay the cost of patterns when they are not used
final boolean hasPatterns = template.hasPatterns();
// Names associated to each column
final Map, List> columnToNames = hasPatterns ? new HashMap<>() : null;
// Read raw data, convert it, check it, and put converted result into data
for (final Map.Entry entry : rawData.entrySet()) {
// Retrieve column name
final String name = entry.getKey();
if (template.containsMatchingColumn(name)) {
// Retrieve associated column template
final ColumnTemplate> column = template.getMatchingColumn(name);
if (hasPatterns) {
// Associate name to this column
columnToNames.computeIfAbsent(column, k -> new ArrayList<>()).add(name);
}
Object value;
if (ERASE.equals(entry.getValue())) {
value = ERASE;
} else {
// Convert raw data
try {
value = column.getImportConverter().apply(entry.getValue());
} catch (final RuntimeException e) {
// Conversion failed
addIssue(ImportIssueType.NON_CONVERTIBLE_DATA,
"Failed to convert '" + entry.getValue() + "' to " + column.getDataType().getCanonicalName() + ".",
name);
value = null;
}
}
// Check converted data
if (value != null && value != ERASE && column.hasChecker()) {
final boolean valid = column.getCheckerOrNull().testRaw(value);
if (!valid) {
// Check failed
addIssue(ImportIssueType.NON_COMPLIANT_DATA,
column.getCheckFailureSeverity(),
"Check [" + column.getCheckerOrNull().explain(true, "?") + "] failed for '?'=" + value + ".",
name);
}
}
// Put converted data, even if check failed
if (value != null) {
this.data.put(name, value);
}
}
// The raw data is ignored and not converted: should we generate an issue?
}
final ImportAction action = getAction(Checks.isNotNull(builder.defaultAction, "defaultAction"));
// Check that columns are set / unset in accordance with their usage (that depends on action)
// If action is not correctly set, this may be incomplete
if (hasPatterns) {
for (final ColumnTemplate> column : template.getColumns()) {
final List names = columnToNames.computeIfAbsent(column, k -> new ArrayList<>());
if (names.isEmpty()) {
if (column.isName()) {
names.add(column.getName());
} else {
// Pattern column without any associated data
checkMissingPattern(column, action);
}
}
for (final String name : names) {
check(column, name, action);
}
}
} else {
for (final ColumnTemplate> column : template.getColumns()) {
check(column, column.getName(), action);
}
}
}
private void check(ColumnTemplate> column,
String name,
ImportAction action) {
final Object value = this.data.get(name);
if (column.getUsage().isMandatoryFor(action)) {
// Column is mandatory for action
if (value == null) {
addIssue(ImportIssueType.MISSING_MANDATORY_DATA,
"No data for mandatory column '" + name + "'.",
column.getName());
} else if (value == ERASE) {
addIssue(ImportIssueType.UNEXPECTED_ERASE,
unexpected(ERASE, "mandatory", name, action),
name);
}
} else if (column.getUsage().isOptionalFor(action)) {
// Column is optional for action
if (value == ERASE
&& (action == ImportAction.CREATE
|| action == ImportAction.DELETE
|| action == ImportAction.UPDATE && !column.getUsage().isErasable())) {
addIssue(ImportIssueType.UNEXPECTED_ERASE,
unexpected(ERASE, "optional", name, action),
name);
}
} else {
// Column is ignored for action
if (value == ERASE) {
addIssue(ImportIssueType.UNEXPECTED_ERASE,
unexpected(ERASE, "ignored", name, action),
name);
} else if (value != null) {
addIssue(ImportIssueType.UNEXPECTED_DATA,
unexpected("data", "ignored", name, action),
name);
}
}
}
/**
* Checks a pattern column that has no actual data.
*
* @param column The pattern column.
* @param action The action.
*/
private void checkMissingPattern(ColumnTemplate> column,
ImportAction action) {
if (column.getUsage().isMandatoryFor(action)) {
// Column is mandatory for action
addIssue(ImportIssueType.MISSING_MANDATORY_DATA,
"No data for mandatory pattern column '" + column.getLabel() + "'.",
column.getLabel());
}
}
private static String unexpected(String content,
String columnKind,
String columnName,
ImportAction action) {
return "Unexpected " + content + " in " + columnKind + "column '" + columnName + "' when action is " + action + ".";
}
private void addIssue(ImportIssueType type,
IssueSeverity severity,
String description,
String columnName) {
if (issues == null) {
issues = new ArrayList<>();
}
issues.add(ImportIssues.builder()
.name(type)
.severity(severity)
.description(description)
.addLocation(WorkbookLocation.builder()
.sheetName(getSheetName())
.columnName(columnName)
.rowNumber(getNumber())
.systemId(getSystemId())
.build())
.build());
}
private void addIssue(ImportIssueType type,
String description,
String columnName) {
addIssue(type,
type.getSeverity(),
description,
columnName);
}
@Override
public String getSystemId() {
return systemId;
}
@Override
public String getSheetName() {
return sheetName == null ? getTemplate().getName() : sheetName;
}
@Override
public SheetTemplateInstance getTemplateInstance() {
return templateInstance;
}
@Override
public SheetTemplate getTemplate() {
return templateInstance.getTemplate();
}
@Override
public Set getNames() {
return rawData.keySet();
}
List getSortedNames() {
return getNames().stream().sorted().toList();
}
@Override
public int getNumber() {
return number;
}
@Override
public ImportAction getAction(Optional defaultAction) {
final ImportAction def = defaultAction.isPresent()
? defaultAction.get()
: getTemplate().getDefaultAction();
return getData(ImportAction.class,
getTemplate().getActionColumnName(),
def);
}
@Override
public boolean isErase(String name) {
Checks.isNotNull(name, ImpExNames.NAME);
return data.get(name) == ERASE;
}
@Override
public String getRawDataOrNull(String name) {
Checks.isNotNull(name, ImpExNames.NAME);
return rawData.get(name);
}
@Override
public Object getDataOrNull(String name) {
Checks.isNotNull(name, ImpExNames.NAME);
final Object value = data.get(name);
return value == ERASE ? null : value;
}
@Override
public T getDataOrNull(Class cls,
String name) {
Checks.isNotNull(cls, ImpExNames.CLS);
Checks.isNotNull(name, ImpExNames.NAME);
return cls.cast(getDataOrNull(name));
}
@Override
public List getIssues() {
return issues == null
? Collections.emptyList()
: issues;
}
@Override
public boolean canBeProcessed() {
if (issues == null) {
return true;
} else {
for (final Issue issue : issues) {
if (issue.getSeverity().isAtLeast(IssueSeverity.CRITICAL)) {
return false;
}
}
return true;
}
}
@Override
public boolean isEmpty() {
return data.isEmpty();
}
@Override
public WorkbookLocation getLocation() {
return WorkbookLocation.builder()
.systemId(getSystemId())
.sheetName(getSheetName())
.rowNumber(getNumber())
.build();
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("[")
.append(getNumber())
.append(" {");
boolean first = true;
for (final String name : getSortedNames()) {
if (first) {
first = false;
} else {
builder.append(", ");
}
builder.append(name)
.append("=")
.append(getDataOrNull(name));
}
builder.append("}]");
return builder.toString();
}
public static Builder builder() {
return new Builder();
}
public static final class Builder {
private Optional defaultAction = Optional.empty();
private String systemId;
private SheetTemplateInstance templateInstance;
private String sheetName;
private int number;
private final Map rawData = new HashMap<>();
private Builder() {
}
public Builder defaultAction(ImportAction defaultAction) {
return defaultAction(Optional.ofNullable(defaultAction));
}
public Builder defaultAction(Optional defaultAction) {
this.defaultAction = defaultAction;
return this;
}
public Builder systemId(String systemId) {
this.systemId = systemId;
return this;
}
public Builder templateInstance(SheetTemplateInstance templateInstance) {
this.templateInstance = templateInstance;
return this;
}
public Builder sheetName(String sheetName) {
this.sheetName = sheetName;
return this;
}
public Builder number(int number) {
this.number = number;
return this;
}
public Builder put(String name,
String value) {
this.rawData.put(name, value);
return this;
}
public Builder put(ColumnTemplate> column,
String value) {
column.checkIsName();
this.rawData.put(column.getName(), value);
return this;
}
public ImportRow build() {
return new ImportRowImpl(this);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy