TableParsers.java

  1. package org.jbehave.core.model;

  2. import java.util.ArrayList;
  3. import java.util.Deque;
  4. import java.util.HashMap;
  5. import java.util.LinkedList;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.Optional;
  9. import java.util.function.UnaryOperator;
  10. import java.util.regex.Matcher;

  11. import org.apache.commons.lang3.StringUtils;
  12. import org.apache.commons.text.translate.CharSequenceTranslator;
  13. import org.apache.commons.text.translate.LookupTranslator;
  14. import org.jbehave.core.configuration.Keywords;
  15. import org.jbehave.core.i18n.LocalizedKeywords;
  16. import org.jbehave.core.model.ExamplesTable.TableProperties;
  17. import org.jbehave.core.model.ExamplesTable.TablePropertiesQueue;
  18. import org.jbehave.core.model.ExamplesTable.TableRows;
  19. import org.jbehave.core.steps.ParameterConverters;

  20. public class TableParsers {

  21.     private static final String ROW_SEPARATOR_PATTERN = "\r?\n";

  22.     private static final CharSequenceTranslator UNESCAPE_TRANSLATOR;

  23.     static {
  24.         final Map<CharSequence, CharSequence> unescapeMapping = new HashMap<>();
  25.         unescapeMapping.put("\\\\", "\\");
  26.         unescapeMapping.put("\\", StringUtils.EMPTY);
  27.         unescapeMapping.put("\\n", "\n");
  28.         unescapeMapping.put("\\r", "\r");
  29.         UNESCAPE_TRANSLATOR = new LookupTranslator(unescapeMapping);
  30.     }

  31.     private final Keywords keywords;
  32.     private final ParameterConverters parameterConverters;
  33.     private final Optional<String> defaultNullPlaceholder;

  34.     public TableParsers(ParameterConverters parameterConverters) {
  35.         this(new LocalizedKeywords(), parameterConverters);
  36.     }

  37.     public TableParsers(Keywords keywords, ParameterConverters parameterConverters) {
  38.         this(keywords, parameterConverters, Optional.empty());
  39.     }

  40.     public TableParsers(Keywords keywords, ParameterConverters parameterConverters,
  41.             Optional<String> defaultNullPlaceholder) {
  42.         this.keywords = keywords;
  43.         this.parameterConverters = parameterConverters;
  44.         this.defaultNullPlaceholder = defaultNullPlaceholder;
  45.     }

  46.     public TablePropertiesQueue parseProperties(String tableAsString) {
  47.         Deque<TableProperties> properties = new LinkedList<>();
  48.         String tableWithoutProperties = tableAsString.trim();
  49.         Matcher matcher = ExamplesTable.INLINED_PROPERTIES_PATTERN.matcher(tableWithoutProperties);
  50.         while (matcher.matches()) {
  51.             String propertiesAsString = matcher.group(1);
  52.             propertiesAsString = StringUtils.replace(propertiesAsString, "\\{", "{");
  53.             propertiesAsString = StringUtils.replace(propertiesAsString, "\\}", "}");
  54.             properties.add(new TableProperties(propertiesAsString, keywords, parameterConverters));
  55.             tableWithoutProperties = matcher.group(2).trim();
  56.             matcher = ExamplesTable.INLINED_PROPERTIES_PATTERN.matcher(tableWithoutProperties);
  57.         }
  58.         if (properties.isEmpty()) {
  59.             properties.add(new TableProperties("", keywords, parameterConverters));
  60.         }
  61.         return new TablePropertiesQueue(tableWithoutProperties, properties);
  62.     }

  63.     public TableRows parseRows(String tableAsString, TableProperties properties) {
  64.         List<String> headers = new ArrayList<>();
  65.         List<List<String>> rows = new ArrayList<>();

  66.         for (String rowLine : tableAsString.split(ROW_SEPARATOR_PATTERN)) {
  67.             String trimmedRowLine = rowLine.trim();
  68.             // skip ignorable or empty lines
  69.             if (!trimmedRowLine.startsWith(properties.getIgnorableSeparator()) && !trimmedRowLine.isEmpty()) {
  70.                 if (headers.isEmpty()) {
  71.                     headers.addAll(parseRow(trimmedRowLine, true, properties));
  72.                 } else {
  73.                     List<String> cells = parseRow(trimmedRowLine, false, properties);
  74.                     if (cells.size() > headers.size()) {
  75.                         cells = cells.subList(0, headers.size());
  76.                     }
  77.                     rows.add(cells);
  78.                 }
  79.             }
  80.         }

  81.         return new TableRows(headers, rows);
  82.     }

  83.     public List<String> parseRow(String rowAsString, boolean header, TableProperties properties) {
  84.         String separator = header ? properties.getHeaderSeparator() : properties.getValueSeparator();
  85.         String commentSeparator = properties.getCommentSeparator();
  86.         Optional<String> nullPlaceholder = properties.getNullPlaceholder().map(Optional::of).orElse(
  87.                 defaultNullPlaceholder);
  88.         UnaryOperator<String> trimmer = properties.isTrim() ? String::trim : UnaryOperator.identity();
  89.         boolean processEscapeSequences = properties.isProcessEscapeSequences();
  90.         String[] cells = StringUtils.splitByWholeSeparatorPreserveAllTokens(rowAsString.trim(), separator);
  91.         List<String> row = new ArrayList<>(cells.length);
  92.         for (int i = 0; i < cells.length; i++) {
  93.             String cell = cells[i];
  94.             cell = StringUtils.substringBefore(cell, commentSeparator);
  95.             if ((i == 0 || i == cells.length - 1) && cell.isEmpty()) {
  96.                 continue;
  97.             }
  98.             String trimmedCell = trimmer.apply(cell);
  99.             if (processEscapeSequences) {
  100.                 trimmedCell = UNESCAPE_TRANSLATOR.translate(trimmedCell);
  101.             }
  102.             row.add(nullPlaceholder.filter(trimmedCell::equals).isPresent() ? null : trimmedCell);
  103.         }
  104.         return row;
  105.     }
  106. }