package org.lucares.pdbui; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.EnumSet; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.function.Function; import org.lucares.pdbui.domain.TagMatcher; import org.lucares.utils.Preconditions; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; public final class CsvReaderSettings { public static final String ISO_8601 = "ISO-8601"; private final static ObjectMapper OBJECT_MAPPER = new ObjectMapper(); public static String stripPrefixDefault(final String value) { if (value.startsWith("Default")) { return value.replaceFirst("Default", ""); } return value; } public enum PostProcessors { LOWER_CASE(String::toLowerCase), STRIP(String::trim), STRIP_PREFIX_DEFAULT(CsvReaderSettings::stripPrefixDefault); private final Function function; PostProcessors(final Function function) { this.function = function; } public Function getFunction() { return function; } public static Function toFunction(final EnumSet postProcessors) { if (postProcessors == null || postProcessors.isEmpty()) { return Function.identity(); } final Iterator it = postProcessors.iterator(); Function result = it.next().getFunction(); while (it.hasNext()) { final Function next = it.next().getFunction(); result = result.andThen(next); } return result; } } public static final class ColumnDefinitions { Map columns = new HashMap<>(); public Map getColumns() { return columns; } public void setColumns(final Map columnDefinitions) { this.columns = columnDefinitions; } public void ignoreColumn(final String csvColumnHeader) { columns.putIfAbsent(csvColumnHeader, new ColumnDefinition()); columns.get(csvColumnHeader).setIgnore(true); } public void rename(final String csvColumnHeader, final String renameTo) { columns.putIfAbsent(csvColumnHeader, new ColumnDefinition()); columns.get(csvColumnHeader).setRenameTo(renameTo); } public void postProcess(final String csvColumnHeader, final EnumSet postProcessors) { columns.putIfAbsent(csvColumnHeader, new ColumnDefinition()); columns.get(csvColumnHeader).setPostProcessors(postProcessors); } public boolean isIgnoredColumn(final String csvColumnHeader) { return columns.getOrDefault(csvColumnHeader, new ColumnDefinition()).isIgnore(); } public String getRenameTo(final String csvColumnHeader) { return columns.getOrDefault(csvColumnHeader, new ColumnDefinition()).getRenameTo(); } public EnumSet getPostProcessors(final String csvColumnHeader) { return columns.getOrDefault(csvColumnHeader, new ColumnDefinition()).getPostProcessors(); } @Override public String toString() { final StringBuilder result = new StringBuilder(); for (final String col : columns.keySet()) { result.append(col); result.append(":"); result.append(columns.get(col)); result.append("\n"); } return result.toString(); } } public static final class ColumnDefinition { private boolean ignore; private String renameTo; private EnumSet postProcessors = EnumSet.noneOf(PostProcessors.class); public ColumnDefinition() { super(); } public boolean isIgnore() { return ignore; } public void setIgnore(final boolean ignore) { this.ignore = ignore; } public String getRenameTo() { return renameTo; } public void setRenameTo(final String renameTo) { this.renameTo = renameTo; } public EnumSet getPostProcessors() { return postProcessors != null ? postProcessors : EnumSet.noneOf(PostProcessors.class); } public void setPostProcessors(final EnumSet postProcessors) { this.postProcessors = postProcessors; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); if (ignore) { builder.append(" ignore="); builder.append(ignore); } if (renameTo != null) { builder.append(" renameTo="); builder.append(renameTo); } if (postProcessors != null && !postProcessors.isEmpty()) { builder.append(" postProcess="); builder.append(postProcessors); } return builder.toString(); } } private String separator; private Character quoteCharacter = null; private ColumnDefinitions columnDefinitions = new ColumnDefinitions(); private Map additionalTags = new HashMap(); private String timeColumn; private String valueColumn; private String comment = "#"; private String dateTimePattern = ISO_8601; private final List firstLineMatcher = new ArrayList<>(); public CsvReaderSettings() { this("@timestamp", "duration", ",", new ColumnDefinitions()); } private CsvReaderSettings(final String timeColumn, final String valueColumn, final String separator, final ColumnDefinitions columnDefinitions) { this.timeColumn = timeColumn; this.valueColumn = valueColumn; this.separator = separator; this.columnDefinitions = columnDefinitions; } public static CsvReaderSettings create(final String timeColumn, final String valueColumn, final String separator, final ColumnDefinitions columnDefinitions) { Preconditions.checkTrue(separator.getBytes(StandardCharsets.UTF_8).length == 1, "Only separators that fulfill separator == (byte)separator are supported. " + "This restriction is because the parsing algorithm skips the overhead of " + "translating bytes to characters."); return new CsvReaderSettings(timeColumn, valueColumn, separator, columnDefinitions); } public String getTimeColumn() { return timeColumn; } public void setTimeColumn(final String timeColumn) { this.timeColumn = timeColumn; } public String getValueColumn() { return valueColumn; } public void setValueColumn(final String valueColumn) { this.valueColumn = valueColumn; } public String getSeparator() { return separator; } public void setSeparator(final String separator) { this.separator = separator; } public byte separatorByte() { final byte[] bytes = separator.getBytes(StandardCharsets.UTF_8); Preconditions.checkEqual(bytes.length, 1, "separator must be a character that is represented as a single byte in UTF-8"); return bytes[0]; } public String getComment() { return comment; } public void setComment(final String comment) { this.comment = comment; } public byte commentByte() { final byte[] bytes = comment.getBytes(StandardCharsets.UTF_8); Preconditions.checkEqual(bytes.length, 1, "comment must be a character that is represented as a single byte in UTF-8"); return bytes[0]; } public void putAdditionalTag(final String field, final String value) { additionalTags.put(field, value); } public void putAdditionalTag(final Map additionalTags) { this.additionalTags.putAll(additionalTags); } public Map getAdditionalTags() { return Map.copyOf(additionalTags); } public void setAdditionalTags(final Map additionalTags) { this.additionalTags = additionalTags; } public ColumnDefinitions getColumnDefinitions() { return columnDefinitions; } public void setColumnDefinitions(final ColumnDefinitions columnDefinitions) { this.columnDefinitions = columnDefinitions; } public List getFirstLineMatcher() { return firstLineMatcher; } public void setFirstLineMatcher(final Collection firstLineMatchers) { this.firstLineMatcher.clear(); this.firstLineMatcher.addAll(firstLineMatchers); } public void addFirstLineMatcher(final TagMatcher tagMatcher) { this.firstLineMatcher.add(tagMatcher); } /** * The quote character. If null then no quoting is allowed. * * @param quoteCharacter */ public void setQuoteCharacter(final Character quoteCharacter) { this.quoteCharacter = quoteCharacter; } /** * The quote character. If null then no quoting is allowed. * * @return the quote character */ public Character getQuoteCharacter() { return quoteCharacter; } public String getDateTimePattern() { return dateTimePattern; } public void setDateTimePattern(final String dateTimePattern) { this.dateTimePattern = dateTimePattern; } public CsvReaderSettings copy() { try { final String json = OBJECT_MAPPER.writeValueAsString(this); return OBJECT_MAPPER.readValue(json, CsvReaderSettings.class); } catch (final JsonProcessingException e) { throw new IllegalStateException(e); } } @Override public String toString() { final StringBuilder builder = new StringBuilder(); if (separator != null) { builder.append("\nseparator="); builder.append(separator); builder.append(", "); } if (quoteCharacter != null) { builder.append("\nquoteCharacter="); builder.append(quoteCharacter); } else { builder.append("\nno quotes"); } if (columnDefinitions != null) { builder.append("\ncolumnDefinitions="); builder.append(columnDefinitions); builder.append(", "); } if (additionalTags != null) { builder.append("\nadditionalTags="); builder.append(additionalTags); builder.append(", "); } if (timeColumn != null) { builder.append("\ntimeColumn="); builder.append(timeColumn); builder.append(", "); } if (valueColumn != null) { builder.append("\nvalueColumn="); builder.append(valueColumn); builder.append(", "); } if (firstLineMatcher != null) { builder.append("\nfirstLineMatcher="); builder.append(firstLineMatcher); builder.append(", "); } if (comment != null) { builder.append("\ncomment="); builder.append(comment); } return builder.toString(); } }