package gherkin.formatter; import gherkin.formatter.model.Background; import gherkin.formatter.model.BasicStatement; import gherkin.formatter.model.Examples; import gherkin.formatter.model.Feature; import gherkin.formatter.model.Range; import gherkin.formatter.model.Row; import gherkin.formatter.model.Scenario; import gherkin.formatter.model.ScenarioOutline; import gherkin.formatter.model.Step; import gherkin.formatter.model.Tag; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; public class FilterFormatter implements Formatter { private final Formatter formatter; private final Filter filter; private List<Tag> featureTags; private List<Tag> featureElementTags; private List<Tag> examplesTags; private List<BasicStatement> featureEvents; private List<BasicStatement> backgroundEvents; private List<BasicStatement> featureElementEvents; private List<BasicStatement> examplesEvents; private String featureName; private String featureElementName; private String examplesName; private Range featureElementRange; private Range examplesRange; public FilterFormatter(Formatter formatter, List filters) { this.formatter = formatter; this.filter = detectFilter(filters); featureTags = new ArrayList<Tag>(); featureElementTags = new ArrayList<Tag>(); examplesTags = new ArrayList<Tag>(); featureEvents = new ArrayList<BasicStatement>(); backgroundEvents = new ArrayList<BasicStatement>(); featureElementEvents = new ArrayList<BasicStatement>(); examplesEvents = new ArrayList<BasicStatement>(); } private Filter detectFilter(List filters) { Set<Class> filterClasses = new HashSet<Class>(); for (Object filter : filters) { filterClasses.add(filter.getClass()); } if (filterClasses.size() > 1) { throw new IllegalArgumentException("Inconsistent filters: " + filters + ". Only one type [line,name,tag] can be used at once."); } Class<?> typeOfFilter = filters.get(0).getClass(); if (String.class.isAssignableFrom(typeOfFilter)) { return new TagFilter(filters); } else if (Number.class.isAssignableFrom(typeOfFilter)) { return new LineFilter(filters); } else if (Pattern.class.isAssignableFrom(typeOfFilter)) { return new PatternFilter(filters); } else { throw new RuntimeException("Could not create filter method for unknown filter of type: " + typeOfFilter); } } @Override public void uri(String uri) { formatter.uri(uri); } @Override public void feature(Feature feature) { featureTags = feature.getTags(); featureName = feature.getName(); featureEvents = new ArrayList<BasicStatement>(); featureEvents.add(feature); } @Override public void background(Background background) { featureElementName = background.getName(); featureElementRange = background.getLineRange(); backgroundEvents = new ArrayList<BasicStatement>(); backgroundEvents.add(background); } @Override public void scenario(Scenario scenario) { replay(); featureElementTags = scenario.getTags(); featureElementName = scenario.getName(); featureElementRange = scenario.getLineRange(); featureElementEvents = new ArrayList<BasicStatement>(); featureElementEvents.add(scenario); } @Override public void scenarioOutline(ScenarioOutline scenarioOutline) { replay(); featureElementTags = scenarioOutline.getTags(); featureElementName = scenarioOutline.getName(); featureElementRange = scenarioOutline.getLineRange(); featureElementEvents = new ArrayList<BasicStatement>(); featureElementEvents.add(scenarioOutline); } @Override public void examples(Examples examples) { replay(); examplesTags.addAll(examples.getTags()); examplesName = examples.getName(); Range tableBodyRange; switch (examples.getRows().size()) { case 0: tableBodyRange = new Range(examples.getLineRange().getLast(), examples.getLineRange().getLast()); break; case 1: tableBodyRange = new Range(examples.getRows().get(0).getLine(), examples.getRows().get(0).getLine()); break; default: tableBodyRange = new Range(examples.getRows().get(1).getLine(), examples.getRows().get(examples.getRows().size() - 1).getLine()); } examplesRange = new Range(examples.getLineRange().getFirst(), tableBodyRange.getLast()); if (filter.evaluate(Collections.<Tag>emptyList(), Collections.<String>emptyList(), Collections.singletonList(tableBodyRange))) { examples.setRows(filter.filterTableBodyRows(examples.getRows())); } examplesEvents = new ArrayList<BasicStatement>(); examplesEvents.add(examples); } @Override public void step(Step step) { if (!featureElementEvents.isEmpty()) { featureElementEvents.add(step); } else { backgroundEvents.add(step); } featureElementRange = new Range(featureElementRange.getFirst(), step.getLineRange().getLast()); } public void table(List<Row> table) { } @Override public void eof() { replay(); formatter.eof(); } @Override public void syntaxError(String state, String event, List<String> legalEvents, String uri, Integer line) { throw new UnsupportedOperationException(); } @Override public void done() { formatter.done(); } @Override public void close() { formatter.close(); } @Override public void startOfScenarioLifeCycle(Scenario scenario) { // NoOp } @Override public void endOfScenarioLifeCycle(Scenario scenario) { // NoOp } private void replay() { List<Tag> feTags = new ArrayList<Tag>(featureTags); feTags.addAll(featureElementTags); List<String> feNames = Arrays.asList(featureName, featureElementName); List<Range> feRanges = Arrays.asList(featureElementRange); boolean featureElementOk = filter.evaluate(feTags, feNames, feRanges); List<Tag> exTags = new ArrayList<Tag>(feTags); exTags.addAll(examplesTags); List<String> exNames = new ArrayList<String>(feNames); exNames.add(examplesName); List<Range> exRanges = new ArrayList<Range>(feRanges); exRanges.add(examplesRange); boolean examplesOk = filter.evaluate(exTags, exNames, exRanges); if (featureElementOk || examplesOk) { replayEvents(featureEvents); replayEvents(backgroundEvents); replayEvents(featureElementEvents); if (examplesOk) { replayEvents(examplesEvents); } } examplesEvents.clear(); examplesTags.clear(); examplesName = null; examplesRange = null; } private void replayEvents(List<BasicStatement> events) { for (BasicStatement event : events) { event.replay(formatter); } events.clear(); } }