JsonAliasParser.java

  1. package org.jbehave.core.parsers;

  2. import static java.util.stream.Collectors.collectingAndThen;
  3. import static java.util.stream.Collectors.groupingBy;
  4. import static java.util.stream.Collectors.toList;
  5. import static org.apache.commons.lang3.Validate.isTrue;

  6. import java.lang.reflect.Type;
  7. import java.util.Collection;
  8. import java.util.Collections;
  9. import java.util.List;
  10. import java.util.Objects;
  11. import java.util.Set;
  12. import java.util.stream.Collectors;

  13. import com.google.gson.Gson;
  14. import com.google.gson.reflect.TypeToken;

  15. import org.jbehave.core.configuration.Keywords;
  16. import org.jbehave.core.model.Alias;
  17. import org.jbehave.core.model.AliasVariant;
  18. import org.jbehave.core.steps.StepType;

  19. public class JsonAliasParser implements AliasParser {

  20.     private final Keywords keywords;

  21.     public JsonAliasParser(Keywords keywords) {
  22.         this.keywords = keywords;
  23.     }

  24.     @SuppressWarnings("unchecked")
  25.     @Override
  26.     public Collection<Alias> parse(Set<String> aliasesAsStrings) {
  27.         if (aliasesAsStrings.isEmpty()) {
  28.             return Collections.emptyList();
  29.         }

  30.         Gson gson = new Gson();
  31.         Type aliasType = new TypeToken<List<AliasContainer>>() {}.getType();

  32.         return aliasesAsStrings.stream()
  33.                                .map(json -> (List<AliasContainer>) gson.fromJson(json, aliasType))
  34.                                .flatMap(List::stream)
  35.                                .map(JsonAliasParser::validate)
  36.                                .collect(groupingBy(AliasContainer::getName, collectingAndThen(toList(),
  37.                                        JsonAliasParser::mergeAliases)))
  38.                                .values()
  39.                                .stream()
  40.                                .map(this::convert)
  41.                                .collect(toList());
  42.     }

  43.     // https://github.com/google/gson/issues/61
  44.     private static AliasContainer validate(AliasContainer alias) {
  45.         isTrue(alias.getName() != null, "The 'name' property that identifies step must be set");
  46.         Set<AliasEntry> aliases = alias.getAliases();
  47.         isTrue(aliases != null, "The 'aliases' property must be set");
  48.         aliases.forEach(entry -> isTrue(entry.getName() != null, "The 'name' property of alias must be set"));
  49.         return alias;
  50.     }

  51.     private static AliasContainer mergeAliases(List<AliasContainer> aliases) {
  52.         if (aliases.size() == 1) {
  53.             return aliases.get(0);
  54.         }

  55.         return aliases.stream().reduce((l, r) -> {
  56.             l.getAliases().addAll(r.getAliases());
  57.             return l;
  58.         }).get();
  59.     }

  60.     private Alias convert(AliasContainer container) {
  61.         String name = container.getName();
  62.         StepType stepType = keywords.stepTypeFor(name);

  63.         List<AliasVariant> variants = container.getAliases().stream().map(alias -> {
  64.             String aliasName = alias.getName();
  65.             StepType aliasType = keywords.stepTypeFor(alias.getName());

  66.             if (aliasType != stepType) {
  67.                 throw new InvalidAliasType(aliasName, stepType);
  68.             }

  69.             return keywords.stepWithoutStartingWord(aliasName, aliasType);
  70.         }).map(AliasVariant::new).collect(Collectors.toList());

  71.         return new Alias(keywords.stepWithoutStartingWord(name, stepType), stepType, variants);
  72.     }

  73.     @SuppressWarnings("unused")
  74.     private static final class AliasContainer {

  75.         private String name;
  76.         private Set<AliasEntry> aliases;

  77.         private String getName() {
  78.             return name;
  79.         }

  80.         private void setName(String name) {
  81.             this.name = name;
  82.         }

  83.         private Set<AliasEntry> getAliases() {
  84.             return aliases;
  85.         }

  86.         private void setAliases(Set<AliasEntry> aliases) {
  87.             this.aliases = aliases;
  88.         }

  89.     }

  90.     @SuppressWarnings("unused")
  91.     private static final class AliasEntry {

  92.         private String name;

  93.         private String getName() {
  94.             return name;
  95.         }

  96.         private void setName(String name) {
  97.             this.name = name;
  98.         }

  99.         @Override
  100.         public int hashCode() {
  101.             return Objects.hash(name);
  102.         }

  103.         @Override
  104.         public boolean equals(Object obj) {
  105.             if (this == obj) {
  106.                 return true;
  107.             }

  108.             if (!(obj instanceof AliasEntry)) {
  109.                 return false;
  110.             }

  111.             AliasEntry other = (AliasEntry) obj;
  112.             return Objects.equals(name, other.name);
  113.         }

  114.     }

  115.     @SuppressWarnings("serial")
  116.     public static class InvalidAliasType extends RuntimeException {

  117.         public InvalidAliasType(String alias, StepType stepType) {
  118.             super(String.format("The alias '%s' must be of type '%s'", alias, stepType));
  119.         }

  120.     }
  121. }