lib/cucumber/parser/gherkin_builder.rb



require 'cucumber/ast'
require 'gherkin/rubify'
require 'cucumber/ast/multiline_argument'
require 'cucumber/ast/empty_background'

module Cucumber
  module Parser
    # This class conforms to the Gherkin event API and builds the
    # "legacy" AST. It will be replaced later when we have a new "clean"
    # AST.
    class GherkinBuilder
      include Gherkin::Rubify

      def initialize(path = 'UNKNOWN-FILE')
        @path = path
      end

      def result
        return nil unless @feature_builder
        @feature_builder.result(language)
      end

      def language=(language)
        @language = language
      end

      def uri(uri)
        @path = uri
      end

      def feature(node)
        @feature_builder = FeatureBuilder.new(file, node)
      end

      def background(node)
        builder = BackgroundBuilder.new(file, node)
        @feature_builder.background_builder = builder
        @current = builder
      end

      def scenario(node)
        builder = ScenarioBuilder.new(file, node)
        @feature_builder.add_child builder
        @current = builder
      end

      def scenario_outline(node)
        builder = ScenarioOutlineBuilder.new(file, node)
        @feature_builder.add_child builder
        @current = builder
      end

      def examples(examples)
        examples_fields = [
          Ast::Location.new(file, examples.line),
          Ast::Comment.new(examples.comments.map{|comment| comment.value}.join("\n")),
          examples.keyword,
          examples.name,
          examples.description,
          matrix(examples.rows)
        ]
        @current.add_examples examples_fields, examples
      end

      def step(node)
        builder = StepBuilder.new(file, node)
        @current.add_child builder
      end

      def eof
      end

      def syntax_error(state, event, legal_events, line)
        # raise "SYNTAX ERROR"
      end

      private

      if defined?(JRUBY_VERSION)
        java_import java.util.ArrayList
        ArrayList.__persistent__ = true
      end

      def matrix(gherkin_table)
        gherkin_table.map do |gherkin_row|
          row = gherkin_row.cells
          class << row
            attr_accessor :line
          end
          row.line = gherkin_row.line
          row
        end
      end

      def language
        @language || raise("Language has not been set")
      end

      def file
        if Cucumber::WINDOWS && !ENV['CUCUMBER_FORWARD_SLASH_PATHS']
          @path.gsub(/\//, '\\')
        else
          @path
        end
      end

      class Builder
        def initialize(file, node)
          @file, @node = file, node
        end

        private

        def tags
          Ast::Tags.new(nil, node.tags)
        end

        def location
          Ast::Location.new(file, node.line)
        end

        def comment
          Ast::Comment.new(node.comments.map{ |comment| comment.value }.join("\n"))
        end

        attr_reader :file, :node
      end

      class FeatureBuilder < Builder
        def result(language)
          background = background(language)
          feature = Ast::Feature.new(
            location,
            background,
            comment,
            tags,
            node.keyword,
            node.name.lstrip,
            node.description.rstrip,
            children.map { |builder| builder.result(background, language, tags) }
          )
          feature.gherkin_statement(node)
          feature.language = language
          feature
        end

        def background_builder=(builder)
          @background_builder = builder
        end

        def add_child(child)
          children << child
        end

        def children
          @children ||= []
        end

        private

        def background(language)
          return Ast::EmptyBackground.new unless @background_builder
          @background ||= @background_builder.result(language)
        end
      end

      class BackgroundBuilder < Builder
        def result(language)
          background = Ast::Background.new(
            language,
            location,
            comment,
            node.keyword,
            node.name,
            node.description,
            steps(language)
          )
          background.gherkin_statement(node)
          background
        end

        def steps(language)
          children.map { |child| child.result(language) }
        end

        def add_child(child)
          children << child
        end

        def children
          @children ||= []
        end

      end

      class ScenarioBuilder < Builder
        def result(background, language, feature_tags)
          scenario = Ast::Scenario.new(
            language,
            location,
            background,
            comment,
            tags,
            feature_tags,
            node.keyword,
            node.name,
            node.description,
            steps(language)
          )
          scenario.gherkin_statement(node)
          scenario
        end

        def steps(language)
          children.map { |child| child.result(language) }
        end

        def add_child(child)
          children << child
        end

        def children
          @children ||= []
        end
      end

      class ScenarioOutlineBuilder < Builder
        def result(background, language, feature_tags)
          scenario_outline = Ast::ScenarioOutline.new(
            language,
            location,
            background,
            comment,
            tags,
            feature_tags,
            node.keyword,
            node.name,
            node.description,
            steps(language),
            examples_sections
          )
          scenario_outline.gherkin_statement(node)
          scenario_outline
        end

        def add_examples(examples_section, node)
          @examples_sections ||= []
          @examples_sections << [examples_section, node]
        end

        def steps(language)
          children.map { |child| child.result(language) }
        end

        def add_child(child)
          children << child
        end

        def children
          @children ||= []
        end

        private

        attr_reader :examples_sections
      end

      class StepBuilder < Builder
        def result(language)
          step = Ast::Step.new(
            language,
            location,
            node.keyword,
            node.name,
            Ast::MultilineArgument.from(node.doc_string || node.rows)
          )
          step.gherkin_statement(node)
          step
        end
      end

    end
  end
end