require 'json'
require 'socket'
require 'openssl'
require 'timeout'

require_relative 'frames/error'
require_relative 'frames/message'
require_relative 'frames/response'
require_relative 'logger'

module Nsq
  class Connection
    include Nsq::AttributeLogger
    @@log_attributes = [:host, :port]

    attr_reader :host
    attr_reader :port
    attr_accessor :max_in_flight
    attr_reader :presumed_in_flight

    USER_AGENT = "nsq-ruby/#{Nsq::Version::STRING}"
    RESPONSE_HEARTBEAT = '_heartbeat_'
    RESPONSE_OK = 'OK'

    def initialize(opts = {})
      @host = opts[:host] || (raise ArgumentError, 'host is required')
      @port = opts[:port] || (raise ArgumentError, 'port is required')
      @queue = opts[:queue]
      @topic = opts[:topic]
      @channel = opts[:channel]
      @msg_timeout = opts[:msg_timeout] || 60_000 # 60s
      @max_in_flight = opts[:max_in_flight] || 1
      @tls_options = opts[:tls_options]
      @max_attempts = opts[:max_attempts]
      if opts[:ssl_context]
        if @tls_options
          warn 'ssl_context and tls_options both set. Using tls_options. Ignoring ssl_context.'
        else
          @tls_options = opts[:ssl_context]
          warn 'ssl_context will be deprecated nsq-ruby version 3. Please use tls_options instead.'
        end
      end
      @tls_v1 = !!opts[:tls_v1]

      if @tls_options
        if @tls_v1
          validate_tls_options!
        else
          warn 'tls_options was provided, but tls_v1 is false. Skipping validation of tls_options.'
        end
      end

      if @msg_timeout < 1000
        raise ArgumentError, 'msg_timeout cannot be less than 1000. it\'s in milliseconds.'
      end

      # for outgoing communication
      @write_queue = SizedQueue.new(10000)

      # For indicating that the connection has died.
      # We use a Queue so we don't have to poll. Used to communicate across
      # threads (from write_loop and read_loop to connect_and_monitor).
      @death_queue = Queue.new

      @connected = false
      @presumed_in_flight = 0

      open_connection
      start_monitoring_connection
    end

    def connected?
      @connected
    end

    # close the connection and don't try to re-open it
    def close
      stop_monitoring_connection
      close_connection
    end

    def sub(topic, channel)
      write "SUB #{topic} #{channel}\n"
    end

    def rdy(count)
      write "RDY #{count}\n"
    end

    def fin(message_id)
      write "FIN #{message_id}\n"
      decrement_in_flight
    end

    def req(message_id, timeout)
      write "REQ #{message_id} #{timeout}\n"
      decrement_in_flight
    end

    def touch(message_id)
      write "TOUCH #{message_id}\n"
    end

    def pub(topic, message)
      write ["PUB #{topic}\n", message.bytesize, message].pack('a*l>a*')
    end

    def dpub(topic, delay_in_ms, message)
      write ["DPUB #{topic} #{delay_in_ms}\n", message.bytesize, message].pack('a*l>a*')
    end

    def mpub(topic, messages)
      body = messages.map do |message|
        [message.bytesize, message].pack('l>a*')
      end.join

      write ["MPUB #{topic}\n", body.bytesize, messages.size, body].pack('a*l>l>a*')
    end

    # Tell the server we are ready for more messages!
    def re_up_ready
      rdy(@max_in_flight)
      # assume these messages are coming our way. yes, this might not be the
      # case, but it's much easier to manage our RDY state with the server if
      # we treat things this way.
      @presumed_in_flight = @max_in_flight
    end

    private

    def cls
      write "CLS\n"
    end

    def nop
      write "NOP\n"
    end

    def write(raw)
      @write_queue.push(raw)
    end

    def write_to_socket(raw)
      debug ">>> #{raw.inspect}"
      @socket.write(raw)
    end

    def identify
      hostname = Socket.gethostname
      metadata = {
        client_id: hostname,
        hostname: hostname,
        feature_negotiation: true,
        heartbeat_interval: 30_000, # 30 seconds
        output_buffer: 16_000, # 16kb
        output_buffer_timeout: 250, # 250ms
        tls_v1: @tls_v1,
        snappy: false,
        deflate: false,
        sample_rate: 0, # disable sampling
        user_agent: USER_AGENT,
        msg_timeout: @msg_timeout
      }.to_json
      write_to_socket ["IDENTIFY\n", metadata.length, metadata].pack('a*l>a*')

      # Now wait for the response!
      frame = receive_frame
      server = JSON.parse(frame.data)

      if @max_in_flight > server['max_rdy_count']
        raise "max_in_flight is set to #{@max_in_flight}, server only supports #{server['max_rdy_count']}"
      end

      @server_version = server['version']
    end

    def handle_response(frame)
      if frame.data == RESPONSE_HEARTBEAT
        debug 'Received heartbeat'
        nop
      elsif frame.data == RESPONSE_OK
        debug 'Received OK'
      else
        die "Received response we don't know how to handle: #{frame.data}"
      end
    end

    def receive_frame
      if buffer = @socket.read(8)
        size, type = buffer.unpack('l>l>')
        size -= 4 # we want the size of the data part and type already took up 4 bytes
        data = @socket.read(size)
        frame_class = frame_class_for_type(type)
        return frame_class.new(data, self)
      end
    end

    FRAME_CLASSES = [Response, Error, Message]
    def frame_class_for_type(type)
      raise "Bad frame type specified: #{type}" if type > FRAME_CLASSES.length - 1
      [Response, Error, Message][type]
    end

    def decrement_in_flight
      @presumed_in_flight -= 1

      if server_needs_rdy_re_ups?
        # now that we're less than @max_in_flight we might need to re-up our RDY state
        threshold = (@max_in_flight * 0.2).ceil
        re_up_ready if @presumed_in_flight <= threshold
      end
    end

    def start_read_loop
      @read_loop_thread ||= Thread.new{read_loop}
    end

    def stop_read_loop
      @read_loop_thread.kill if @read_loop_thread
      @read_loop_thread = nil
    end

    def read_loop
      loop do
        frame = receive_frame
        if frame.is_a?(Response)
          handle_response(frame)
        elsif frame.is_a?(Error)
          error "Error received: #{frame.data}"
        elsif frame.is_a?(Message)
          debug "<<< #{frame.body}"
          if @max_attempts && frame.attempts > @max_attempts
            fin(frame.id)
          else
            @queue.push(frame) if @queue
          end
        else
          raise 'No data from socket'
        end
      end
    rescue Exception => ex
      die(ex)
    end

    def start_write_loop
      @write_loop_thread ||= Thread.new{write_loop}
    end

    def stop_write_loop
      if @write_loop_thread
        @write_queue.push(:stop_write_loop)
        @write_loop_thread.join
      end
      @write_loop_thread = nil
    end

    def write_loop
      data = nil
      loop do
        data = @write_queue.pop
        break if data == :stop_write_loop
        write_to_socket(data)
      end
    rescue Exception => ex
      # requeue PUB and MPUB commands
      if data =~ /^M?PUB/
        debug "Requeueing to write_queue: #{data.inspect}"
        @write_queue.push(data)
      end
      die(ex)
    end

    # Waits for death of connection
    def start_monitoring_connection
      @connection_monitor_thread ||= Thread.new{monitor_connection}
      @connection_monitor_thread.abort_on_exception = true
    end

    def stop_monitoring_connection
      @connection_monitor_thread.kill if @connection_monitor_thread
      @connection_monitor = nil
    end

    def monitor_connection
      loop do
        # wait for death, hopefully it never comes
        cause_of_death = @death_queue.pop
        warn "Died from: #{cause_of_death}"

        debug 'Reconnecting...'
        reconnect
        debug 'Reconnected!'

        # clear all death messages, since we're now reconnected.
        # we don't want to complete this loop and immediately reconnect again.
        @death_queue.clear
      end
    end

    # close the connection if it's not already closed and try to reconnect
    # over and over until we succeed!
    def reconnect
      close_connection
      with_retries do
        open_connection
      end
    end

    def open_connection
      @socket = TCPSocket.new(@host, @port)
      # write the version and IDENTIFY directly to the socket to make sure
      # it gets to nsqd ahead of anything in the `@write_queue`
      write_to_socket '  V2'
      identify
      upgrade_to_ssl_socket if @tls_v1

      start_read_loop
      start_write_loop
      @connected = true

      # we need to re-subscribe if there's a topic specified
      if @topic
        debug "Subscribing to #{@topic}"
        sub(@topic, @channel)
        re_up_ready
      end
    end

    # closes the connection and stops listening for messages
    def close_connection
      cls if connected?
      stop_read_loop
      stop_write_loop
      @socket.close if @socket
      @socket = nil
      @connected = false
    end

    # this is called when there's a connection error in the read or write loop
    # it triggers `connect_and_monitor` to try to reconnect
    def die(reason)
      @connected = false
      @death_queue.push(reason)
    end

    def upgrade_to_ssl_socket
      ssl_opts = [@socket, openssl_context].compact
      @socket = OpenSSL::SSL::SSLSocket.new(*ssl_opts)
      @socket.sync_close = true
      @socket.connect
    end

    def openssl_context
      return unless @tls_options

      context = OpenSSL::SSL::SSLContext.new
      context.cert = OpenSSL::X509::Certificate.new(File.read(@tls_options[:certificate]))
      context.key = OpenSSL::PKey::RSA.new(File.read(@tls_options[:key]))
      if @tls_options[:ca_certificate]
        context.ca_file = @tls_options[:ca_certificate]
      end
      context.verify_mode = @tls_options[:verify_mode] || OpenSSL::SSL::VERIFY_NONE
      context
    end

    # Retry the supplied block with exponential backoff.
    #
    # Borrowed liberally from:
    # https://github.com/ooyala/retries/blob/master/lib/retries.rb
    def with_retries(&block)
      base_sleep_seconds = 0.5
      max_sleep_seconds = 300 # 5 minutes

      # Let's do this thing
      attempts = 0

      begin
        attempts += 1
        return block.call(attempts)

      rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EHOSTUNREACH,
             Errno::ENETDOWN, Errno::ENETUNREACH, Errno::ETIMEDOUT, Timeout::Error => ex

        raise ex if attempts >= 100

        # The sleep time is an exponentially-increasing function of base_sleep_seconds.
        # But, it never exceeds max_sleep_seconds.
        sleep_seconds = [base_sleep_seconds * (2 ** (attempts - 1)), max_sleep_seconds].min
        # Randomize to a random value in the range sleep_seconds/2 .. sleep_seconds
        sleep_seconds = sleep_seconds * (0.5 * (1 + rand()))
        # But never sleep less than base_sleep_seconds
        sleep_seconds = [base_sleep_seconds, sleep_seconds].max

        warn "Failed to connect: #{ex}. Retrying in #{sleep_seconds.round(1)} seconds."

        snooze(sleep_seconds)

        retry
      end
    end

    # Se we can stub for testing and reconnect in a tight loop
    def snooze(t)
      sleep(t)
    end

    def server_needs_rdy_re_ups?
      # versions less than 0.3.0 need RDY re-ups
      # see: https://github.com/bitly/nsq/blob/master/ChangeLog.md#030---2014-11-18
      major, minor = @server_version.split('.').map(&:to_i)
      major == 0 && minor <= 2
    end

    def validate_tls_options!
      [:key, :certificate].each do |key|
        unless @tls_options.has_key?(key)
          raise ArgumentError.new "@tls_options requires a :#{key}"
        end
      end

      [:key, :certificate, :ca_certificate].each do |key|
        if @tls_options[key] && !File.readable?(@tls_options[key])
          raise LoadError.new "@tls_options :#{key} is unreadable"
        end
      end
    end
  end
end


# frozen_string_literal: true
module SkinnyControllers
  # This class provides a way to determine all names / classes of operations
  # and policies based on he given information.
  #
  # The class methods show what is required for each scenario
  class Lookup
    include EnsureExistence
    include Policy

    class << self
      def from_controller(controller_class:, verb:, model_class:)
        Lookup.new(
          controller_class_name: controller_class.name,
          verb:                  verb,
          model_class:           model_class
        )
      end

      def from_operation(operation_class:, model_class:)
        qualified_name = operation_class.name
        parts = qualified_name.split('::')
        operation_name = parts[-2]
        operation_parts = operation_name.split(SkinnyControllers.operations_suffix)

        Lookup.new(
          verb:            parts.last,
          # namespace:       parts[0..-3],
          operation_name:  operation_name,
          operation_class: operation_class,
          model_class:     model_class,
          namespace:       qualified_name.deconstantize.deconstantize
        )
      end
    end

    # @param [Hash] args - all possible parameters
    # - @option [String] controller_class_name
    # - @option [Class] controller_class
    # - @option [String] verb
    # - @option [String] operation_name
    # - @option [String] model_name
    # - @option [Class] model_class
    # - @option [Hash] options
    def initialize(args = {})
      @controller_class_name = args[:controller_class_name]
      @controller_class = args[:controller_class]
      @verb_for_action = args[:verb]
      @operation_name = args[:operation_name]
      @operation_class = args[:operation_class]
      @model_name = args[:model_name]
      @namespace = args[:namespace]
      @model_class = args[:model_class]
      @policy_method_name = args[:policy_method_name]
      @options = args[:options] || {}
    end

    # PostsController
    # => PostOperations::Verb
    #
    # Api::V2::PostsController
    # => Api::V2::PostOperations::Verb
    def operation_class
      @operation_class ||= begin
        found_namespace = ensure_namespace!(operation_namespace)

        operation = namespaced_operation_name.split(found_namespace.name).last
        qualified_name = found_namespace ? found_namespace.name + operation : namespaced_operation_name

        ensure_operation_class!(qualified_name)
      end
    end

    def policy_class
      @policy_class ||= begin
        ensure_namespace!(namespace) if namespace.present?
        ensure_policy_class!(namespaced_policy_name)
      end
    end

    def policy_method_name
      @policy_method_name ||= @verb_for_action.underscore + POLICY_METHOD_SUFFIX
    end

    def model_class
      @model_class ||= @options[:model_class] || model_name.safe_constantize
    end

    def model_name
      @model_name ||= @model_class.try(:name) || resource_name
    end

    # @return [String] name of the supposed operation class
    def namespaced_operation_name
      @namespaced_operation_name ||= [
        operation_namespace,
        @verb_for_action
      ].reject(&:blank?).join('::')
    end

    def namespaced_policy_name
      @namespaced_policy_name ||= [
        namespace,
        "#{resource_name}#{SkinnyControllers.policy_suffix}"
      ].reject(&:blank?).join('::')
    end

    def operation_namespace
      @operation_namespace ||= [
        namespace,
        operation_name
      ].reject(&:blank?).join('::')
    end

    def operation_name
      @operation_name ||= "#{resource_name}#{SkinnyControllers.operations_suffix}"
    end

    # @return [String] the namespace
    def namespace
      @namespace ||= begin
        resource_parts.length > 1 ? resource_parts[0..-2].join('::') : ''
      end
    end

    def resource_name
      @resource_name ||= resource_parts && resource_parts[-1].singularize ||
        @model_class&.name ||
        operation_parts && operation_parts[-1].singularize
    end

    # PostsController
    # => Posts
    #
    # Api::V2::PostsController
    # => Api, V2, Posts
    def resource_parts
      @resource_parts ||= controller_class_name&.split(/::|Controller/)
    end

    def operation_parts
      @operation_parts ||= operation_namespace&.split(/::|Operations/)
    end

    def controller_class_name
      @controller_class_name ||= @controller_class&.name
    end
  end
end


# frozen_string_literal: true
module SkinnyControllers
  module Diet
    extend ActiveSupport::Concern

    ALLOWED_OPTIONS = [
      :model_class, :parent_class,
      :asociation_name,
      :model_params_key
    ].freeze

    included do
      class << self
        attr_accessor :options

        def skinny_controllers_config(options = {})
          @options = options.select { |o| ALLOWED_OPTIONS.include?(o) }
        end
      end
    end

    def create_operation(user:, params_for_action: nil)
      operation_class.new(
        user,
        params,
        params_for_action,
        action_name,
        _lookup,
        _options
      )
    end

    # TODO: what if we want multiple operations per action?
    #
    # @return an instance of the operation with default parameters
    def operation
      @operation ||= operation_class.new(
        current_user,
        params, params_for_action,
        action_name,
        _lookup,
        _options
      )
    end

    # Assumes the operation name from the controller name
    #
    # @example SomeObjectsController => Operation::SomeObject::Action
    # @return [Class] the operation class for the model and verb
    def operation_class
      _lookup.operation_class
    end

    def _options
      self.class.options || {}
    end

    def _lookup
      @_lookup ||= Lookup.from_controller(
        controller_class: self.class,
        verb:             verb_for_action,
        model_class:      _options[:model_class]
      )
    end

    # abstraction for `operation.run`
    # useful when there is no logic needed for deciding what to
    # do with an operation or if there is no logic to decide which operation
    # to use
    #
    # @return [ActiveRecord::Base] the model
    def model
      @model ||= operation.run
    end

    private

    # In order of most specific, to least specific:
    # - {action}_{model_name}_params
    # - {action}_params
    # - {model_key}_params
    # - resource_params
    # - params
    #
    # It's recommended to use whitelisted strong parameters on
    # actions such as create and update
    #
    # @return the whitelisted strong parameters object
    def params_for_action
      return {} if action_name == 'destroy'

      key = _options[:model_params_key]
      # model_class should be a class
      klass = _options[:model_class]

      model_key =
        if key.present?
          key
        elsif klass
          klass.name.underscore
        else
          _lookup.model_name.underscore
        end

      params_lookups = [
        # e.g.: create_post_params
        "#{action_name}_#{model_key}_params",
        # generic for action
        "#{action_name}_params",
        # e.g.: post_params
        "#{model_key}_params",
        # most generic
        'resource_params'
      ]

      lookup_params_for_action(params_lookups)
    end

    def lookup_params_for_action(lookups)
      lookups.each do |method_name|
        return send(method_name) if respond_to?(method_name, true)
      end

      params
    end

    # action name is inherited from ActionController::Base
    # http://www.rubydoc.info/docs/rails/2.3.8/ActionController%2FBase%3Aaction_name

    def verb_for_action
      SkinnyControllers.action_map[action_name] ||
        (action_name && action_name.classify) ||
        SkinnyControllers.action_map['default']
    end
  end
end


#--
#     This file is part of the X12Parser library that provides tools to
#     manipulate X12 messages using Ruby native syntax.
#
#     http://x12parser.rubyforge.org
#
#     Copyright (C) 2008 APP Design, Inc.
#
#     This library is free software; you can redistribute it and/or
#     modify it under the terms of the GNU Lesser General Public
#     License as published by the Free Software Foundation; either
#     version 2.1 of the License, or (at your option) any later version.
#
#     This library is distributed in the hope that it will be useful,
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#     Lesser General Public License for more details.
#
#     You should have received a copy of the GNU Lesser General Public
#     License along with this library; if not, write to the Free Software
#     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
#++
#

module X12
  #
  # A class for parsing X12 message definition expressed in XML format.
  #

  class XMLDefinitions < Hash

    # Parse definitions out of XML file
    def initialize(str)
      doc = LibXML::XML::Document.string(str)
      definitions = doc.root.name =~ /^Definition$/i ? doc.root.find('*').to_a : [doc.root]

      definitions.each { |element|
        #puts element.name
        syntax_element = case element.name
                         when /table/i
                           parse_table(element)
                         when /segment/i
                           parse_segment(element)
                         when /composite/i
                           parse_composite(element)
                         when /loop/i
                           parse_loop(element)
                         end
        self[syntax_element.class] ||= {}
        self[syntax_element.class][syntax_element.name]=syntax_element
      }
    end # initialize

    private

    def parse_boolean(s)
      return case s
             when nil
               false
             when ""
               false
             when /(^y(es)?$)|(^t(rue)?$)|(^1$)/i
               true
             when /(^no?$)|(^f(alse)?$)|(^0$)/i
               false
             else
               nil
             end # case
    end #parse_boolean

    def parse_type(s)
      return case s
             when nil
               'string'
             when /^C.+$/
               s
             when /^i(nt(eger)?)?$/i
               'int'
             when /^l(ong)?$/i
               'long'
             when /^d(ouble)?$/i
               'double'
             when /^s(tr(ing)?)?$/i
               'string'
             else
               nil
             end # case
    end #parse_type

    def parse_int(s)
      return case s
             when nil then 0
             when /^\d+$/ then s.to_i
             when /^inf(inite)?$/ then 999999
             else
               nil
             end # case
    end #parse_int

    def parse_attributes(e)
      throw Exception.new("No name attribute found for : #{e.inspect}")          unless name = e.attributes["name"]
      throw Exception.new("Cannot parse attribute 'min' for: #{e.inspect}")      unless min = parse_int(e.attributes["min"])
      throw Exception.new("Cannot parse attribute 'max' for: #{e.inspect}")      unless max = parse_int(e.attributes["max"])
      throw Exception.new("Cannot parse attribute 'type' for: #{e.inspect}")     unless type = parse_type(e.attributes["type"])
      throw Exception.new("Cannot parse attribute 'required' for: #{e.inspect}") if (required = parse_boolean(e.attributes["required"])).nil?

      validation = e.attributes["validation"]
      min = 1 if required and min < 1
      max = 999999 if max == 0

      return name, min, max, type, required, validation
    end # parse_attributes

    def parse_field(e)
      name, min, max, type, required, validation = parse_attributes(e)

      # FIXME - for compatibility with d12 - constants are stored in attribute 'type' and are enclosed in
      # double quotes
      const_field =  e.attributes["const"]
      if(const_field)
        type = "\"#{const_field}\""
      end

      Field.new(name, type, required, min, max, validation)
    end # parse_field

    def parse_table(e)
      name, min, max, type, required, validation = parse_attributes(e)

      content = e.find("Entry").inject({}) {|t, entry|
        t[entry.attributes["name"]] = entry.attributes["value"]
        t
      }
      Table.new(name, content)
    end

    def parse_segment(e)
      name, min, max, type, required, validation = parse_attributes(e)

      fields = e.find("Field").inject([]) {|f, field|
        f << parse_field(field)
      }
      Segment.new(name, fields, Range.new(min, max))
    end

    def parse_composite(e)
      name, min, max, type, required, validation = parse_attributes(e)

      fields = e.find("Field").inject([]) {|f, field|
        f << parse_field(field)
      }
      Composite.new(name, fields)
    end

    def parse_loop(e)
      name, min, max, type, required, validation = parse_attributes(e)

      components = e.find('*').to_a.inject([]){|r, element|
        r << case element.name
             when /loop/i
               parse_loop(element)
             when /segment/i
               parse_segment(element)
             else
               throw Exception.new("Cannot recognize syntax for: #{element.inspect} in loop #{e.inspect}")
             end # case
      }
      Loop.new(name, components, Range.new(min, max))
    end

  end # Parser
end


#--
#     This file is part of the X12Parser library that provides tools to
#     manipulate X12 messages using Ruby native syntax.
#
#     http://x12parser.rubyforge.org
#
#     Copyright (C) 2008 APP Design, Inc.
#
#     This library is free software; you can redistribute it and/or
#     modify it under the terms of the GNU Lesser General Public
#     License as published by the Free Software Foundation; either
#     version 2.1 of the License, or (at your option) any later version.
#
#     This library is distributed in the hope that it will be useful,
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#     Lesser General Public License for more details.
#
#     You should have received a copy of the GNU Lesser General Public
#     License along with this library; if not, write to the Free Software
#     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
#++
#
module X12

  #
  # Base class for Segment, Composite, and Loop. Contains setable
  # segment_separator, field_separator, and composite_separator fields.
  #

  class Base

    attr_reader   :name, :repeats
    attr_accessor :segment_separator, :field_separator, :composite_separator, :next_repeat, :parsed_str, :nodes

    # Creates a new base element with a given name, array of sub-elements, and array of repeats if any.
    def initialize(name, arr, repeats = nil)
      @nodes = arr
      @name = name
      @repeats = repeats
      @next_repeat = nil        # Next repeat of the same element, if any
      @parsed_str = nil

      @segment_separator   = '~'
      @field_separator     = '*'
      @composite_separator = ':'

      #puts "Created #{name} #{object_id} #{self.class}  "
    end

    # Formats a printable string containing the base element's content
    def inspect
      "#{self.class.to_s.sub(/^.*::/, '')} (#{name}) #{repeats} #{super.inspect[1..-2]} =<#{parsed_str}, #{next_repeat.inspect}> ".gsub(/\\*\"/, '"')
    end

    # Prints a tree-like representation of the element
    def show(ind = '')
      count = 0
      self.to_a.each{|i|
        #puts "#{ind}#{i.name} #{i.object_id} #{i.super.object_id} [#{count}]: #{i.parsed_str} #{i.super.class}"
        puts "#{ind}#{i.name} [#{count}]: #{i.to_s.sub(/^(.{30})(.*?)(.{30})$/, '\1...\3')}"
        # Force parsing a segment
        if i.kind_of?(X12::Segment) && i.nodes[0]
          i.find_field(i.nodes[0].name)
        end
        i.nodes.each{|j|
          case
          when j.kind_of?(X12::Base)  then j.show(ind+'  ')
          when j.kind_of?(X12::Field) then puts "#{ind+'  '}#{j.name} -> '#{j.to_s}'"
          end
        }
        count += 1
      }
    end

    # Try to parse the current element one more time if required. Returns the rest of the string
    # or the same string if no more repeats are found or required.
    def do_repeats(s)
      if self.repeats.end > 1
        possible_repeat = self.dup
        p_s = possible_repeat.parse(s)
        if p_s
          s = p_s
          self.next_repeat = possible_repeat
        end # if parsed
      end # more repeats
      s
    end # do_repeats

    # Empty out the current element
    def set_empty!
      @next_repeat = nil
      @parsed_str = nil
      self
    end

    # Make a deep copy of the element
    def dup
      n = clone
      n.set_empty!
      n.nodes = n.nodes.dup
      n.nodes.each_index{|i|
        n.nodes[i] = n.nodes[i].dup
        n.nodes[i].set_empty!
      }
      #puts "Duped #{self.class} #{self.name} #{self.object_id} #{super.object_id} -> #{n.name} #{n.super.object_id} #{n.object_id} "
      n
    end # dup

    # Recursively find a sub-element, which also has to be of type Base.
    def find(e)
      #puts "Finding [#{e}] in #{self.class} #{name}"
      case self
        when X12::Loop
        # Breadth first
        res = nodes.find{|i| e==i.name }
        return res if res
        # Depth now
        nodes.each{|i|
          res = i.find(e) if i.kind_of?(X12::Loop)
          return res unless res.nil? or EMPTY==res # otherwise keep looping
        }
        when X12::Segment
        return find_field(e).to_s
      end # case
      return EMPTY
    end

    # Present self and all repeats as an array with self being #0
    def to_a
      res = [self]
      nr = self.next_repeat
      while nr do
        res << nr
        nr = nr.next_repeat
      end
      res
    end

    # Returns a parsed string representation of the element
    def to_s
      @parsed_str || ''
    end

    # The main method implementing Ruby-like access methods for nested elements
    def method_missing(meth, *args, &block)
      str = meth.id2name
      str = str[1..str.length] if str =~ /^_\d+$/ # to avoid pure number names like 270, 997, etc.
      #puts "Missing #{str}"
      if str =~ /=$/
        # Assignment
        str.chop!
        #puts str
        case self
        when X12::Segment
          res = find_field(str)
          throw Exception.new("No field '#{str}' in segment '#{self.name}'") if EMPTY == res
          res.content = args[0].to_s
          #puts res.inspect
        else
          throw Exception.new("Illegal assignment to #{meth} of #{self.class}")
        end # case
      else
        # Retrieval
        res = find(str)
        yield res if block_given?
        res
      end # if assignment
    end

    # The main method implementing Ruby-like access methods for repeating elements
    def [](*args)
      #puts "squares #{args.inspect}"
      return self.to_a[args[0]] || EMPTY
    end

    # Yields to accompanying block passing self as a parameter.
    def with(&block)
      if block_given?
        yield self
      else
        throw Exception.new("Method 'with' requires a block.")
      end
    end

    # Returns number of repeats
    def size
      return self.to_a.size
    end

    # Check if any of the fields has been set yet
    def has_content?
      self.nodes.find{|i| i.has_content?}
    end

    # Adds a repeat to a segment or loop. Returns a new segment/loop or self if empty.
    def repeat
      res = if self.has_content? # Do not repeat an empty segment
              last_repeat = self.to_a[-1]
              last_repeat.next_repeat = last_repeat.dup
            else
              self
            end
      yield res if block_given?
      res
    end

  end # Base
end


#--
#     This file is part of the X12Parser library that provides tools to
#     manipulate X12 messages using Ruby native syntax.
#
#     http://x12parser.rubyforge.org
#
#     Copyright (C) 2008 APP Design, Inc.
#
#     This library is free software; you can redistribute it and/or
#     modify it under the terms of the GNU Lesser General Public
#     License as published by the Free Software Foundation; either
#     version 2.1 of the License, or (at your option) any later version.
#
#     This library is distributed in the hope that it will be useful,
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#     Lesser General Public License for more details.
#
#     You should have received a copy of the GNU Lesser General Public
#     License along with this library; if not, write to the Free Software
#     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
#++
#
module X12

  #
  # Implements a segment containing fields or composites
  #

  class Segment < Base

    # Parses this segment out of a string, puts the match into value, returns the rest of the string - nil
    # if cannot parse
    def parse(str)
      s = str
      #puts "Parsing segment #{name} from #{s} with regexp [#{regexp.source}]"
      m = regexp.match(s)
      #puts "Matched #{m ? m[0] : 'nothing'}"

      return nil unless m

      s = m.post_match
      self.parsed_str = m[0]
      s = do_repeats(s)

      #puts "Parsed segment "+self.inspect
      return s
    end # parse

    # Render all components of this segment as string suitable for EDI
    def render
      self.to_a.inject(''){|repeat_str, i|
        if i.repeats.begin < 1 and !i.has_content?
          # Skip optional empty segments
          repeat_str
        else
          # Have to render no matter how empty
          repeat_str += i.name+i.nodes.reverse.inject(''){|nodes_str, j|
            field = j.render
            (j.required or nodes_str != '' or field != '') ? field_separator+field+nodes_str : nodes_str
          } + segment_separator
        end
      }
    end # render

    # Returns a regexp that matches this particular segment
    def regexp
      unless @regexp
        if self.nodes.find{|i| i.type =~ /^".+"$/ }
          # It's a very special regexp if there are constant fields
          re_str = self.nodes.inject("^#{name}#{Regexp.escape(field_separator)}"){|s, i|
            field_re = i.simple_regexp(field_separator, segment_separator)+Regexp.escape(field_separator)+'?'
            field_re = "(#{field_re})?" unless i.required
            s+field_re
          } + Regexp.escape(segment_separator)
          @regexp = Regexp.new(re_str)
        else
          # Simple match
          @regexp = Regexp.new("^#{name}#{Regexp.escape(field_separator)}[^#{Regexp.escape(segment_separator)}]*#{Regexp.escape(segment_separator)}")
        end
        #puts sprintf("%s %p", name, @regexp)
      end
      @regexp
    end

    # Finds a field in the segment. Returns EMPTY if not found.
    def find_field(str)
      #puts "Finding field [#{str}] in #{self.class} #{name}"
      # If there is such a field to begin with
      field_num = nil
      self.nodes.each_index{|i|
        field_num = i if str == self.nodes[i].name
      }
      return EMPTY if field_num.nil?
      #puts field_num

      # Parse the segment if not parsed already
      unless @fields
        @fields = self.to_s.chop.split(Regexp.new(Regexp.escape(field_separator)))
        self.nodes.each_index{|i| self.nodes[i].content = @fields[i+1] }
      end
      #puts self.nodes[field_num].inspect
      return self.nodes[field_num]
    end

    # provides loopong through multiple segments within the loop
    def each
      res = self.to_a
      0.upto(res.length - 1) do |x|
        yield res[x]
      end
    end

  end # Segment
end # X12


#--
#     This file is part of the X12Parser library that provides tools to
#     manipulate X12 messages using Ruby native syntax.
#
#     http://x12parser.rubyforge.org
#
#     Copyright (C) 2008 APP Design, Inc.
#
#     This library is free software; you can redistribute it and/or
#     modify it under the terms of the GNU Lesser General Public
#     License as published by the Free Software Foundation; either
#     version 2.1 of the License, or (at your option) any later version.
#
#     This library is distributed in the hope that it will be useful,
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#     Lesser General Public License for more details.
#
#     You should have received a copy of the GNU Lesser General Public
#     License along with this library; if not, write to the Free Software
#     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
#++
#

module X12

  # $Id: Parser.rb 89 2009-05-13 19:36:20Z ikk $
  #
  # Main class for creating X12 parsers and factories.

  class Parser

    # These constitute prohibited file names under Microsoft
    MS_DEVICES = ['CON',  'PRN',  'AUX',  'CLOCK$', 'NUL', 'COM1', 'LPT1', 'LPT2', 'LPT3', 'COM2', 'COM3', 'COM4']

    # Creates a parser out of a definition
    def initialize(file_name)
      save_definition = @x12_definition

      # Deal with Microsoft devices
      # get the current working directory
      base_name = File.basename(file_name, '.xml')
      if MS_DEVICES.find{|i| i == base_name}
        file_name = File.join(File.dirname, "#{base_name}_.xml")
      end
      file_location = File.join(File.dirname(__FILE__), "../../misc", file_name)

      # Read and parse the definition
      str = File.open(file_location, 'r').read
      #@dir_name = File.dirname(File.expand_path(file_name)) # to look up other files if needed
      @x12_definition = X12::XMLDefinitions.new(str)

      # Populate fields in all segments found in all the loops
      @x12_definition[X12::Loop].each_pair{|k, v|
        #puts "Populating definitions for loop #{k}"
        process_loop(v)
      } if @x12_definition[X12::Loop]

      # Merge the newly parsed definition into a saved one, if any.
      if save_definition
        @x12_definition.keys.each{|t|
          save_definition[t] ||= {}
          @x12_definition[t].keys.each{|u|
            save_definition[t][u] = @x12_definition[t][u]
          }
          @x12_definition = save_definition
        }
      end

      #puts PP.pp(self, '')
    end # initialize

    # Parse a loop of a given name out of a string. Throws an exception if the loop name is not defined.
    def parse(loop_name, str)
      loop = @x12_definition[X12::Loop][loop_name]
      #puts "Loops to parse #{@x12_definition[X12::Loop].keys}"
      throw Exception.new("Cannot find a definition for loop #{loop_name}") unless loop
      loop = loop.dup
      loop.parse(str)
      return loop
    end # parse

    # Make an empty loop to be filled out with information
    def factory(loop_name)
      loop = @x12_definition[X12::Loop][loop_name]
      throw Exception.new("Cannot find a definition for loop #{loop_name}") unless loop
      loop = loop.dup
      return loop
    end # factory

    private

    # Recursively scan the loop and instantiate fields' definitions for all its
    # segments
    def process_loop(loop)
      loop.nodes.each{|i|
        case i
          when X12::Loop then process_loop(i)
          when X12::Segment then process_segment(i) unless i.nodes.size > 0
          else return
        end
      }
    end

    # Instantiate segment's fields as previously defined
    def process_segment(segment)
      #puts "Trying to process segment #{segment.inspect}"
      unless @x12_definition[X12::Segment] && @x12_definition[X12::Segment][segment.name]
        # Try to find it in a separate file if missing from the @x12_definition structure
        initialize(segment.name+'.xml')
        segment_definition = @x12_definition[X12::Segment][segment.name]
        throw Exception.new("Cannot find a definition for segment #{segment.name}") unless segment_definition
      else
        segment_definition = @x12_definition[X12::Segment][segment.name]
      end
      segment_definition.nodes.each_index{|i|
        segment.nodes[i] = segment_definition.nodes[i]
        # Make sure we have the validation table if any for this field. Try to read one in if missing.
        table = segment.nodes[i].validation
        if table
          unless @x12_definition[X12::Table] && @x12_definition[X12::Table][table]
            initialize(table+'.xml')
            throw Exception.new("Cannot find a definition for table #{table}") unless @x12_definition[X12::Table] && @x12_definition[X12::Table][table]
          end
        end
      }
    end

  end # Parser
end


#--
#     This file is part of the X12Parser library that provides tools to
#     manipulate X12 messages using Ruby native syntax.
#
#     http://x12parser.rubyforge.org
#
#     Copyright (C) 2008 APP Design, Inc.
#
#     This library is free software; you can redistribute it and/or
#     modify it under the terms of the GNU Lesser General Public
#     License as published by the Free Software Foundation; either
#     version 2.1 of the License, or (at your option) any later version.
#
#     This library is distributed in the hope that it will be useful,
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#     Lesser General Public License for more details.
#
#     You should have received a copy of the GNU Lesser General Public
#     License along with this library; if not, write to the Free Software
#     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
#++
#
module X12

  #
  # Implements nested loops of segments

  class Loop < Base

#     def regexp
#       @regexp ||=
#         Regexp.new(inject(''){|s, i|
#                      puts i.class
#                      s += case i
#                           when X12::Segment: "(#{i.regexp.source}){#{i.repeats.begin},#{i.repeats.end}}"
#                           when X12::Loop:    "(.*?)"
#                           else
#                             ''
#                           end
#                    })
#     end

    # Parse a string and fill out internal structures with the pieces of it. Returns
    # an unparsed portion of the string or the original string if nothing was parsed out.
    def parse(str)
      #puts "Parsing loop #{name}: "+str
      s = str
      nodes.each{|i|
        m = i.parse(s)
        s = m if m
      }
      if str == s
        return nil
      else
        self.parsed_str = str[0..-s.length-1]
        s = do_repeats(s)
      end
      #puts 'Parsed loop '+self.inspect
      return s
    end # parse

    # Render all components of this loop as string suitable for EDI
    def render
      if self.has_content?
        self.to_a.inject(''){|loop_str, i|
          loop_str += i.nodes.inject(''){|nodes_str, j|
            nodes_str += j.render
          }
        }
      else
        ''
      end
    end # render

    # Formats a printable string containing the loops element's content
    # added to provide compatability with ruby > 2.0.0
    def inspect
      "#{self.class.to_s.sub(/^.*::/, '')} (#{name}) #{repeats} =<#{parsed_str}, #{next_repeat.inspect}> ".gsub(/\\*\"/, '"')
    end

    # Provides looping through repeats of a message
    def each
      res = self.to_a
      0.upto(res.length - 1) do |x|
        yield res[x]
      end
    end

  end # Loop
end # X12


require 'pathname'

module SafetyPin
  class Node
    include_class 'javax.jcr.PropertyType'
    include_class 'java.util.Calendar'
    include_class 'java.util.Date'

    attr_reader :j_node

    def self.find(path)
      raise ArgumentError unless path.to_s.start_with?("/")
      Node.new(session.get_node(path.to_s))
    rescue javax.jcr.PathNotFoundException
      nil
    end

    def self.find_or_create(path, primary_type = nil)
      node_blueprint = NodeBlueprint.new(:path => path.to_s, :primary_type => primary_type)
      find(path) || create(node_blueprint)
    end

    def self.exists?(path)
      find(path) != nil
    end

    def self.session
      JCR.session
    end

    def self.build(node_blueprint_or_path_string)
      if node_blueprint_or_path_string.respond_to?(:primary_type, :properties)
        node_blueprint = node_blueprint_or_path_string
      else
        node_blueprint = NodeBlueprint.new(path: node_blueprint_or_path_string)
      end

      raise NodeError.new("NodeBlueprint is nil") if node_blueprint.nil?
      raise NodeError.new("NodeBlueprint has non-absolute path") unless node_blueprint.path.to_s.start_with?("/")
      raise NodeError.new("Node already exists at path: #{node_blueprint.path}") if Node.exists?(node_blueprint.path)

      rel_path_to_root_node = node_blueprint.path.to_s[1..-1]
      node = self.new(session.root_node.add_node(rel_path_to_root_node, node_blueprint.primary_type))
      node.properties = node_blueprint.properties

      node
    rescue javax.jcr.PathNotFoundException => e
      raise NodeError.new("Cannot add a new node to a non-existing parent at #{node_blueprint.path}")
    end

    def self.update(node_blueprint)
      node = find(node_blueprint.path)
      # raise NodeError.new("Cannot retrieve node for update -- might not exist") if node.nil?
      node.properties = node_blueprint.properties
      node.primary_type = node_blueprint.primary_type
      node.save
      node
    end

    def self.create(node_blueprint_or_path_string)
      node = self.build(node_blueprint_or_path_string)
      node.save
      node
    end

    def self.create_parents(path)
      intermediate_paths = []

      current_intermediate_path = Pathname(path)
      while(current_intermediate_path.to_s != "/")
        current_intermediate_path = current_intermediate_path.parent
        intermediate_paths.push(current_intermediate_path)
      end

      results = intermediate_paths.reverse.map do |intermediate_path|
        create(NodeBlueprint.new(:path => intermediate_path.to_s)) unless exists?(intermediate_path)
      end

      session.save

      results
    end

    def self.create_or_update(node_blueprint_or_node_blueprints)
      node_blueprints = Array(node_blueprint_or_node_blueprints)
      node_blueprints.map do |node_blueprint|
        if exists?(node_blueprint.path)
          update(node_blueprint)
        else
          create(node_blueprint)
        end
      end
    end

    def initialize(j_node)
      @j_node = j_node
    end

    def path
      j_node.path
    end

    def session
      JCR.session
    end

    def ==(other_node)
      return false if other_node.nil?
      return false unless other_node.respond_to?(:path)
      self.path == other_node.path
    end

    def parent
      raise NodeError.new("Root node does not have parent") if path == "/"
      Node.new(j_node.parent)
    end

    def parents
      parents = []

      next_parent = self.parent
      parents << next_parent
      while !next_parent.root?
        next_parent = next_parent.parent
        parents << next_parent
      end

      parents
    end

    def root?
      path == "/"
    end

    def children
      child_nodes = []
      j_node.get_nodes.each do |child_j_node|
        child_nodes << Node.new(child_j_node)
      end
      child_nodes
    end

    def descendants
      children.map {|child| [child, child.descendants] }.flatten
    end

    def child(relative_path)
      child_j_node = j_node.get_node(relative_path.to_s)
      Node.new(child_j_node)
    rescue javax.jcr.PathNotFoundException
      nil
    end

    def name
      j_node.name
    end

    def read_attribute(name)
      name = name.to_s
      property = j_node.get_property(name)
      if property_is_multi_valued?(property)
        retrieve_property_multi_value(property)
      else
        retrieve_property_value(property)
      end
    rescue javax.jcr.PathNotFoundException
      raise NilPropertyError.new("#{name} property not found on node")
    end

    def property_is_multi_valued?(property)
      property.values
      true
    rescue javax.jcr.ValueFormatException
      false
    end

    def retrieve_property_multi_value(property)
      property.values.map {|value| retrieve_value(value) }
    end

    def retrieve_property_value(property)
      retrieve_value(property.value)
    end

    def retrieve_value(value)
      property_type = PropertyType.name_from_value(value.type)
      case property_type
      when "String"
        value.string
      when "Boolean"
        value.boolean
      when "Double"
        value.double
      when "Long"
        value.long
      when "Date"
        Time.at(value.date.time.time / 1000)
      when "Name"
        value.string # Not sure if these should be handled differently
      when "Binary"
        value.binary
      when "Decimal"
        value.decimal
      when "Path"
        Pathname(value.string)
      else
        raise PropertyTypeError.new("Unknown property type: #{property_type}")
      end
    end

    def property_single_valued?(name)
      properties.has_key?(name) and !property_multi_valued?(name)
    end

    def property_multi_valued?(name)
      properties.has_key?(name) and properties[name].is_a?(Array)
    end

    def has_property?(name)
      j_node.has_property(name)
    end

    def remove_property(name)
      j_node.get_property(name).remove if has_property?(name)
    end

    def write_attribute(name, value)
      raise PropertyError.new("Illegal operation: cannot change jcr:primaryType property") if name == "jcr:primaryType"
      name = name.to_s

      if value.nil?
        remove_property(name)
        return nil
      end

      # when going from multi to single value
      if property_multi_valued?(name) and !value.is_a?(Array)
        remove_property(name)
      end

      if value.is_a? Array
        remove_property(name)
        values = value
        val_fact = value_factory
        j_values = []
        values.compact.each do |value|
          j_values << val_fact.create_value(value.to_java)
        end
        j_node.set_property(name, j_values.to_java(Java::JavaxJcr::Value))
      elsif value.is_a? Time or value.is_a? Date
        calendar_value = Calendar.instance
        calendar_value.set_time(value.to_java)
        j_node.set_property(name, calendar_value)
      elsif value.is_a?(Symbol) or value.is_a?(Pathname)
        j_node.set_property(name, value.to_s)
      else
        begin
          j_node.set_property(name, value)
        rescue NameError
          raise SafetyPin::PropertyTypeError.new("Property value type of #{value.class} is unsupported")
        end
      end
    end

    def save
      session.save
      not changed?
    end

    def reload
      j_node.refresh(false)
    end
    alias_method :refresh, :reload

    def [](name)
      read_attribute(name)
    end

    def []=(name, value)
      write_attribute(name, value)
    end

    def changed?
      j_node.modified?
    end

    def new?
      j_node.new?
    end

    def properties
      props = {}
      prop_iter = j_node.properties
      while prop_iter.has_next
        prop = prop_iter.next_property
        unless prop.definition.protected?
          prop_name = prop.name
          props[prop_name] = self[prop_name]
        end
      end
      props
    end

    def protected_properties
      props = {}
      prop_iter = j_node.properties
      while prop_iter.has_next
        prop = prop_iter.next_property
        if prop.definition.protected?
          prop_name = prop.name
          props[prop_name] = self[prop_name]
        end
      end
      props
    end

    def properties=(new_props)
      property_names = (properties.keys + new_props.keys).uniq
      property_names.each do |name|
        # REFACTOR ME PLZ
        child_path = Pathname(path.to_s) + name.to_s
        if new_props[name].is_a? Hash
          new_props[name] = convert_hash_to_node_blueprint(new_props[name])
        end

        if new_props[name].respond_to?(:node_blueprint?) and new_props[name].node_blueprint?
          # Handle node blue prints
          node_blueprint = NodeBlueprint.new(:properties => new_props[name].properties,
                                             :path => child_path.to_s,
                                             :primary_type => new_props[name].primary_type)
          if Node.exists?(child_path)
            Node.update(node_blueprint)
          else
            Node.build(node_blueprint)
          end
        else
          # handle everything else
          self[name] = new_props[name]
        end
      end
    end

    # Convert a hash (and it's values recursively) to NodeBlueprints. This is a
    # helper method, allowing a hash to be passed in to Node#properties= when
    # only properties need to be set. One caveat: all node types will default
    # to nt:unstructured.
    def convert_hash_to_node_blueprint(hash)
      hash.keys.each do |key|
        if hash[key].is_a? Hash
          hash[key] = convert_hash_to_node_blueprint(hash[key])
        end
      end
      NodeBlueprint.new(:path => :no_path, :properties => hash)
    end

    def value_factory
      session.value_factory
    end

    def destroy
      path = self.path
      parent_j_node = j_node.parent
      j_node.remove
      parent_j_node.save
      # raise NodeError.new("Unable to destroy #{path} node") unless self.class.find(path).nil?
    rescue javax.jcr.RepositoryException => e
      raise NodeError.new("Unable to destroy #{path} node: #{e.message}")
    end

    def mixin_types
      j_node.mixin_node_types.map(&:name)
    end

    def add_mixin(mixin_name)
      j_node.add_mixin(mixin_name)
    end

    def remove_mixin(mixin_name)
      j_node.remove_mixin(mixin_name)
    end

    def primary_type
      self["jcr:primaryType"]
    end

    def primary_type=(primary_type)
      j_node.set_primary_type(primary_type)
    end

    def find_or_create(name, primary_type = nil)
      path = Pathname(self.path) + name
      self.class.find_or_create(path.to_s, primary_type)
    end

    # Create and return a child node with a given name
    def create(name, node_blueprint = nil)
      Node.create(node_blueprint_for(name, node_blueprint))
    end

    def build(name, node_blueprint = nil)
      Node.build(node_blueprint_for(name, node_blueprint))
    end

    def node_blueprint_for(name, node_blueprint = nil)
      path = Pathname(self.path) + name.to_s

      unless node_blueprint.nil?
        properties = node_blueprint.properties
        primary_type = node_blueprint.primary_type
      end

      NodeBlueprint.new(:path => path.to_s, :properties => properties, :primary_type => primary_type)
    end

    def replace_property(opts, &block)
      opts = {recursive: false}.merge(opts)
      name_pattern = opts.fetch(:name)
      target_pattern = opts.fetch(:target)
      replacement_block = block || lambda {|value| opts.fetch(:replacement) }

      modified = false
      properties.each do |name, value|
        if name.match(name_pattern) and value.match(target_pattern)
          self[name] = replacement_block.call(value)
          modified = true
        end
      end

      modified_nodes = []
      modified_nodes << self if modified
      modified_nodes << children.map {|child_node| child_node.replace_property(opts) } if opts.fetch(:recursive)

      modified_nodes.flatten
    end

    def has_property(name)
      properties.keys.include?(name)
    end

    def inspect
      "#<#{self.class} path=#{path}>"
    end

    def to_s
      inspect
    end

    def remove_attribute(name)
      write_attribute(name, nil)
    end

    def move_within(dest_parent_path, options = {})
      if options.fetch(:auto_rename, false)
        new_name = available_name_within(dest_parent_path)
      else
        new_name = name
      end

      dest_path = Pathname(dest_parent_path) + new_name
      raise NodeError.new("Existing node at destination path #{dest_path.inspect}") if Node.exists? dest_path
      session.move(path, dest_path.to_s)
    end

    def available_name_within(dest_parent_path)
      new_path = Pathname(dest_parent_path) + name
      index = 1
      while Node.exists? new_path
        new_path = Pathname(dest_parent_path) + "#{name}#{index}"
        index += 1
      end

      new_path
    end

    def rename(new_name)
      dest_path = Pathname(path).parent + new_name
      raise NodeError.new("Node name #{new_name.inspect} already taken") if Node.exists? dest_path
      session.move(path, dest_path.to_s)
    end

    def order_before(sibling_name_or_sibling_node)
      sibling_name = extract_sibling_name(sibling_name_or_sibling_node)
      parent.j_node.order_before(name, sibling_name) # put this before that
    end

    def order_after(sibling_name_or_sibling_node)
      sibling_name = extract_sibling_name(sibling_name_or_sibling_node)
      parent.j_node.order_before(name, sibling_name) # put before sibling (preserving the original order of the sibling)
      parent.j_node.order_before(sibling_name, name) # put that before this
    end

    def extract_sibling_name(sibling_name_or_sibling_node)
      if sibling_name_or_sibling_node.respond_to? :name
        unless siblings?(sibling_name_or_sibling_node)
          raise NodeError.new("#{sibling_name_or_sibling_node} not a sibling to #{self}")
        end
        sibling_name = sibling_name_or_sibling_node.name
      else
        unless siblings.map(&:name).include?(sibling_name_or_sibling_node)
          raise NodeError.new("Sibling named #{sibling_name_or_sibling_node.inspect} not found")
        end
        sibling_name = sibling_name_or_sibling_node.to_s
      end
    end

    def siblings?(sibling)
      Pathname(path).parent == Pathname(sibling.path).parent
    end

    def siblings
      siblings = parent.children
      siblings.delete_if {|sibling| sibling.path == path}
    end
  end

  class NodeError < Exception; end
  class PropertyTypeError < Exception; end
  class NilPropertyError < Exception; end
  class PropertyError < Exception; end
end


module Formbuilder
  class ResponseFieldPrice < ResponseField

    after_initialize -> {
      @serialized = true
      @sort_as_numeric = true
      @field_type = 'price'
      @search_type = 'number'
    }

    def render_input(value, opts = {})
      value ||= {}

      str = """

          $

            <input type='text' name='response_fields[#{self[:id]}][dollars]' id='response_fields_#{self[:id]}' value='#{value['dollars']}' />
            Dollars

      """

      unless self.field_options['disable_cents']
        str += """
          .

            <input type='text' name='response_fields[#{self[:id]}][cents]' value='#{value['cents']}' maxlength='2' />
            Cents

        """
      end

      str += """

      """

      str
    end

    def render_entry(value, opts = {})
      "$#{sprintf('%.2f', "#{value['dollars']}.#{value['cents']}".to_f)}"
    end

    # format: [dollars] [cents]
    # only one is required, and it must consist only of numbers
    def validate_response(value)
      if value.select { |k, v| k.in?(['dollars', 'cents']) && v.present? }
              .find { |k, v| (Float(v) rescue nil).nil? }
              .present?

        "isn't a valid price."
      end
    end

    def sortable_value(value)
      "#{value['dollars'] || '0'}.#{value['cents'] || '0'}".to_f
    end

  end
end


module Formbuilder
  module Entry
    extend ActiveSupport::Concern

    included do
      attr_accessor :skip_validation, :only_validate_page

      before_validation :normalize_responses
      validates_with Formbuilder::EntryValidator
      before_save :calculate_responses_text, if: :responses_column_changed?

      scope :order_by_response_field_value, -> (response_field, direction) {
        if response_field.sort_as_numeric
          order("(responses -> '#{response_field.id}_sortable_value') ::numeric #{direction} NULLS LAST")
        else
          order("LOWER(responses -> '#{response_field.id}_sortable_value') #{direction} NULLS LAST")
        end
      }

      scope :order_by_response_field_checkbox_value, -> (response_field, option, direction) {
        order("(CASE WHEN (responses -> '#{response_field.id}_sortable_values_#{option}') = 'true' THEN
                  1
                ELSE
                  0
                END) #{direction}".squish)
      }

      scope :order_by_response_field_table_sum, -> (response_field, column, direction) {
        order("(responses -> '#{response_field.id}_sum_#{column}') ::numeric #{direction}")
      }
    end

    def valid_page?(x)
      self.valid?
      !self.errors_on_page?(x)
    end

    def first_page_with_errors
      if (i = pages_with_errors.find_index { |x| x == true })
        i + 1
      else
        nil
      end
    end

    def pages_with_errors
      form.response_fields_by_page.map do |page|
        page.find { |response_field| self.error_for(response_field).present? } ? true : false
      end
    end

    def errors_on_page?(x)
      pages_with_errors[x - 1] # 0-indexed
    end

    def responses_column
      @responses_column || 'responses'
    end

    def responses_column=(x)
      @responses_column = x
    end

    def get_responses
      send(responses_column)
    end

    def set_responses(x)
      send("#{responses_column}=", x)
    end

    def mark_responses_as_changed!
      send("#{responses_column}_will_change!")
    end

    def responses_column_changed?
      send("#{responses_column}_changed?")
    end

    def responses_column_was
      send("#{responses_column}_was")
    end

    def value_present?(response_field)
      value = self.response_value(response_field)

      # value isn't blank (ignore hashes)
      return true if (value && value.present? && !value.is_a?(Hash))

      # no options are available
      return true if (response_field.options_field && Array(response_field.field_options["options"]).empty?)

      # there is at least one value (for hashes)
      # reject select fields
      return true if (value.is_a?(Hash) && value.reject { |k, v| k.in? ['am_pm', 'country'] }.find { |k, v| v.present? })

      # otherwise, it's not present
      return false
    end

    def response_value(response_field)
      r = get_responses || {}
      value = r[response_field.id.to_s]

      if value
        response_field.serialized ? YAML::load(value) : value
      elsif !value && response_field.serialized && response_field.field_type != 'checkboxes'
        {}
      else # for checkboxes, we need to know the difference between no value and none selected
        nil
      end
    end

    def save_responses(response_field_params, response_fields, opts = {})
      set_responses({}) unless opts[:partial_update]
      response_field_params ||= {}

      response_fields.select { |rf| rf.input_field }.each do |response_field|
        self.save_response(response_field_params[response_field.id.to_s], response_field, response_field_params)
      end
    end

    def save_response(raw_value, response_field, response_field_params = {})
      value = response_field.transform_raw_value(raw_value, self, response_field_params: response_field_params)

      if value.present?
        get_responses["#{response_field.id}"] = response_field.serialized ? value.to_yaml : value
        get_responses["#{response_field.id}_sortable_value"] = response_field.sortable_value(value)
      else
        get_responses.delete("#{response_field.id}")
        get_responses.delete("#{response_field.id}_sortable_value")
      end

      mark_responses_as_changed!
    end

    def destroy_response(response_field)
      response_field.before_response_destroyed(self)
      id = response_field.id.to_s
      set_responses get_responses.reject { |k, v| k.in?([id, "#{id}_sortable_value"]) }
      mark_responses_as_changed!
    end

    def error_for(response_field)
      Array(self.errors.messages[:"#{responses_column}_#{response_field.id}"]).first
    end

    def calculate_responses_text
      return unless self.respond_to?(:"#{responses_column}_text=")
      self.send(:"#{responses_column}_text=", get_responses.select { |k, v| Integer(k) rescue nil }.values.join(' '))
    end

    # for manual use, maybe when migrating
    def calculate_sortable_values
      response_fieldable.input_fields.each do |response_field|
        if (x = response_value(response_field)).present?
          get_responses["#{response_field.id}_sortable_value"] = response_field.sortable_value(x)
        end
      end

      mark_responses_as_changed!
    end

    # Normalizations get run before validation.
    def normalize_responses
      return if form.blank?

      form.response_fields.each do |response_field|
        if (x = self.response_value(response_field))
          response_field.normalize_response(x, get_responses)
        end
      end

      mark_responses_as_changed!
    end

    # Audits get run explicitly.
    def audit_responses
      form.response_fields.each do |response_field|
        response_field.audit_response(self.response_value(response_field), get_responses)
      end

      mark_responses_as_changed!
    end

  end
end


module Hexp
  # Build Hexps using the builder pattern
  #
  class Builder < BasicObject
    include ::Hexp

    # Construct a new builder, and start building
    #
    # The recommended way to call this is through `Hexp.build`. If the block
    # takes an argument, then builder methods need to be called on that variable.
    #
    # @example With an explicit builder
    #   hi = Hexp.build {|html| html.span "Hello" ; html.span " World"}
    #
    # @example Without a builder object
    #   hi = Hexp.build { span "Hello" ; span " World"}
    #
    # @param [Symbol] tag
    #   The tag of the outermost element (optional)
    # @param [Array<Hash,String>] args
    #   Extra arguments, a String for a text node, a Hash for attributes
    #
    # @yieldparam [Hexp::Builder]
    #   If the block takes an argument it will receive the builder object
    #
    # @api private
    #
    def initialize(tag = nil, *args, &block)
      @stack = []
      if tag
        tag!(tag, *args, &block)
      else
        _process(&block) if block
      end
    end

    # Add a tag (HTML element)
    #
    # Typically this is called implicitly through method missing, but in case of
    # name clashes or dynamically generated tags you can call this directly.
    #
    # @example
    #   hexp = Hexp.build :div do
    #     tag!(:p, "Oh the code, such sweet joy it brings")
    #   end
    #   hexp.to_html #=> "

Oh the code, such sweet joy it brings

"
    #
    # @param [Symbol] tag
    #   The tag name, like 'div' or 'head'
    # @param [Array<Hash|String>] args
    #   A hash of attributes, or a string to use inside the tag, or both. Multiple
    #   occurences of each can be specified
    # @param [Proc] block
    #   Builder directives for the contents of the tag
    #
    # @return [nil]
    #
    # @api public
    def tag!(tag, *args, &block)
      text, attributes = nil, {}
      args.each do |arg|
        case arg
        when ::Hash
          attributes.merge!(arg)
        when ::String
          text ||= ''
          text << arg
        end
      end
      @stack << [tag, attributes, text ? [text] : []]
      if block
        _process(&block)
      end
      if @stack.length > 1
        node = @stack.pop
        @stack.last[2] << node
        NodeBuilder.new(node, self)
      else
        NodeBuilder.new(@stack.last, self)
      end
    end

    alias method_missing tag!

    # Add a text node to the tree
    #
    # @example
    #   hexp = Hexp.build do
    #     span do
    #       text! 'Not all who wander are lost'
    #     end
    #   end
    #
    # @param [String] text
    #   the text to add
    #
    # @return [Hexp::Builder] self
    #
    # @api public
    def text!(text)
      _raise_if_empty! "Hexp::Builder needs a root element to add text elements to"
      @stack.last[2] << text.to_s
      self
    end

    # Add Hexp objects to the current tag
    #
    # Any Hexp::Node or other object implementing to_hexp can be added with
    # this operator. Multiple objects can be specified in one call.
    #
    # Nokogiri and Builder allow inserting of strings containing HTML through
    # this operator. Since this would violate the core philosophy of Hexp, and
    # open the door for XSS vulnerabilities, we do not support that usage.
    #
    # If you really want to insert HTML that is already in serialized form,
    # consider parsing it to Hexps first
    #
    # @example
    #   widget = H[:button, "click me!"]
    #   node = Hexp.build :div do |h|
    #     h << widget
    #   end
    #   node.to_html #=>
<button>click me!</button>

    #
    # @param [Array<#to_hexp>] args
    #   Hexpable objects to add to the current tag
    #
    # @return [Hexp::Builder]
    #
    # @api public
    #
    def <<(*args)
      args.each do |arg|
        if arg.respond_to?(:to_hexp)
          @stack.last[2] << arg
          self
        else
          ::Kernel.raise ::Hexp::FormatError, "Inserting literal HTML into a builder with << is deliberately not supported by Hexp"
        end
      end
    end

    # Implement the standard Hexp coercion protocol
    #
    # By implementing this a Builder is interchangeable for a regular node, so
    # you can use it inside other nodes transparently. But you can call this
    # method if you really, really just want the plain {Hexp::Node}
    #
    # @example
    #  Hexp.build { div { text! 'hello' } }.to_hexp # => H[:div, ["hello"]]
    #
    # @return [Hexp::Node]
    #
    # @api public
    def to_hexp
      _raise_if_empty!
      ::Hexp::Node[*@stack.last]
    end

    # Call the block, with a specific value of 'self'
    #
    # If the block takes an argument, then we pass ourselves (the builder) to
    # the block, and call it as a closure. This way 'self' refers to the calling
    # object, and it can reference its own methods and ivars.
    #
    # If the block does not take an argument, then we evaluate it in the context
    # of ourselves (the builder), so unqualified method calls are seen as
    # builder calls.
    #
    # @param block [Proc]
    #
    # @return [nil]
    #
    # @api private
    def _process(&block)
      if block.arity == 1
        block.call(self)
      else
        self.instance_eval(&block)
      end
    end

    # Allow setting HTML classes through method calls
    #
    # @example
    #   Hexp.build do
    #     div.miraculous.wondrous do
    #       hr
    #     end
    #   end
    #
    # @api private
    class NodeBuilder
      # Create new NodeBuilder
      #
      # @param [Array] node
      #   (tag, attrs, children) triplet
      # @param [Hexp::Builder] builder
      #   The parent builder to delegate back
      #
      # @api private
      def initialize(node, builder)
        @node, @builder = node, builder
      end

      # Used for specifying CSS class names
      #
      # @example
      #   Hexp.build { div.strong.warn }.to_hexp
      #   # => H[:div, class: 'strong warn']
      #
      # @param [Symbol] sym
      #   the class to add
      #
      # @return [Hexp::Builder::NodeBuilder] self
      #
      # @api public
      def method_missing(sym, &block)
        attrs = @node[1]
        @node[1] = attrs.merge class: [attrs[:class], sym.to_s].compact.join(' ')
        @builder._process(&block) if block
        self
      end
    end

    # Return a debugging representation
    #
    # Hexp is intended for HTML, so it shouldn't be a problem that this is an
    # actual method. It really helps for debugging or when playing around in
    # irb. If you really want an `<inspect>` tag, use `tag!(:inspect)`.
    #
    # @example
    #   p Hexp.build { div }
    #
    # @return [String]
    #
    # @api public
    def inspect
      "#<Hexp::Builder #{@stack.empty? ? '[]' : to_hexp.inspect}>"
    end

    # Gratefully borrowed from Builder.
    # I'd like to benchmark this singleton class based version vs
    # adding the methods to the class directly, before putting this in.
    #
    # @param sym [Symbol] Name of the method to define
    # @api private
    #
    # def _cache_method_call(sym)
    #   class << self; self; end.class_eval do
    #     unless method_defined?(sym)
    #       define_method(sym) do |*args, &block|
    #         tag!(sym, *args, &block)
    #       end
    #     end
    #   end
    # end

    private

    # Raise an exception if nothing has been built yet
    #
    # @param [String] text
    #   The error message
    #
    # @raise [Hexp::FormatError]
    #   if the builder is converted to a {Hexp::Node} before a root element is
    #   defined.
    #
    # @api private
    def _raise_if_empty!(text = 'Hexp::Builder is lacking a root element.')
      ::Kernel.raise ::Hexp::FormatError, text if @stack.empty?
    end
  end
end


module Hexp
  # A +Hexp::Node+ represents a single element in a HTML syntax tree. It
  # consists of three parts : the {#tag}, the {#attributes} and the {#children}.
  #
  # Instances of +Hexp::Node+ are immutable. Because of this all methods that
  # "alter" the node actually return a new instance, leaving the old one
  # untouched.
  #
  # @example Immutable nodes : the old one is untouched
  #   node = Hexp::Node.new(:div)
  #   node2 = node.add_class('items')
  #   p node  # => H[:div]
  #   p node2 # => H[:div, 'class' => 'items']
  #
  # The +Hexp::Node+ constructor takes a +Symbol+, a +Hash+ and an +Array+ for
  # the {#tag}, {#attributes} and {#children} respectively. However the
  # attributes and children can each be ommitted if they are empty.
  #
  # If the node contains a single child node, then it is not necessary to wrap
  # that child node in an array. Just put the single node in place of the list
  # of children.
  #
  # One can use +H[tag, attrs, children]+ as a
  # shorthand syntax. Finally one can use {Hexp.build Hexp.build} to construct nodes using
  # Ruby blocks, not unlike the Builder or Nokogiri gems.
  #
  # @example Creating Hexp : syntax alternatives and optional parameters
  #   Hexp::Node.new(:div, class: 'foo')
  #   Hexp::Node.new(:div, {class: 'foo'}, "A text node")
  #   Hexp::Node.new(:div, {class: 'foo'}, ["A text node"])
  #   H[:div, {class: 'foo'}, H[:span, {class: 'big'}, "good stuff"]]
  #   H[:div, {class: 'foo'}, [
  #       H[:span, {class: 'big'}, "good stuff"],
  #       H[:a, {href: '/index'}, "go home"]
  #     ]
  #   ]
  #   Hexp.build { div.strong { "Hello, world!" } }
  #
  # Methods that read or alter the attributes Hash are defined in
  # {Hexp::Node::Attributes}. Methods that read or alter the list of child nodes
  # are defined in {Hexp::Node::Children}
  #
  # == CSS selectors
  #
  # When working with large trees of {Hexp::Node} objects, it is convenient to
  # be able to target specific nodes using CSS selector syntax. Hexp supports a
  # subset of the CSS 3 selector syntax, see {Hexp::Node::CssSelection} for info
  # on the supported syntax.
  #
  # For changing, replacing or removing specific nodes based on a CSS selector
  # string, see {Hexp::Node#replace}. To iterate over nodes, see
  # {Hexp::Node#select}.
  #
  class Node
    include Concord::Public.new(:tag, :attributes, :children)
    extend Forwardable

    include Hexp::Node::Attributes
    include Hexp::Node::Children

    alias attrs attributes

    # The HTML tag of this node
    #
    # @example
    #   H[:p].tag #=> :p
    #
    # @return [Symbol]
    # @api public
    #
    #attr_reader :tag

    # The attributes of this node
    #
    # @example
    #   H[:p, class: 'foo'].attributes #=> {'class' => 'foo'}
    #
    # @return [Hash<String, String>]
    # @api public
    #
    #attr_reader :attributes

    # The child nodes of this node
    #
    # @example
    #   H[:p, [ H[:span], 'hello' ]].children
    #   #=> Hexp::List[ H[:span], Hexp::TextNode["hello"] ]
    #
    # @return [Hexp::List]
    # @api public
    #
    #attr_reader :children

    # Main entry point for creating literal hexps
    #
    # At the moment this just redirects to #new, and since Hexp::Node is aliased
    # to H this provides a shorthand for the contructor,
    #
    # @example
    #   H[:span, {attr: 'value'}].
    #
    # Note that while the H[] form is part of the public API and expected to
    # remain, it's implementation might change. In particular H might become
    # a class or module in its own right, so it is recommended to only use
    # this method in its H[] form.
    #
    # @param args [Array] args a Hexp node components
    # @return [Hexp::Node]
    # @api public
    #
    def self.[](*args)
      new(*args)
    end

    alias concord_init initialize

    # Normalize the arguments
    #
    # @param args [Array] args a Hexp node components
    # @return [Hexp::Node]
    #
    # @example
    #    Hexp::Node.new(:p, {'class' => 'foo'}, [[:b, "Hello, World!"]])
    #
    # @api public
    #
    def initialize(*args)
      tag_ok   = args[0].instance_of?(Symbol)
      raise "The tag of node should be a Symbol" unless tag_ok

      attrs_ok = args[1].instance_of?(Hash) &&
                 args[1].all? {|k,v| k.instance_of?(String) && v.instance_of?(String) }

      if attrs_ok && args[2].instance_of?(List)
        concord_init(args[0], args[1], args[2])
      elsif attrs_ok && args[2].instance_of?(Array)
        concord_init(args[0], args[1], List.new(args[2]))
      else
        concord_init(*Normalize.new(args).call)
      end.freeze
    end

    # Standard hexp coercion protocol, return self
    #
    # @example
    #   H[:p].to_hexp #=> H[:p]
    #
    # @return [Hexp::Node] self
    # @api public
    #
    def to_hexp
      self
    end

    # Serialize this node to HTML
    #
    # @example
    #   H[:html, [ H[:body, ["hello, world"] ] ]] .to_html
    #   # => "hello, world"
    #
    # @return [String]
    # @api public
    #
    def to_html(options = {})
      Unparser.new(options).call(self)
    end

    # Convert this node into a Nokogiri Document
    #
    # @example
    #  H[:p].to_dom
    #  #=> #<Nokogiri::HTML::Document name="document"
    #        children=[#<Nokogiri::XML::DTD name="html">,
    #        #<Nokogiri::XML::Element name="p">]>
    #
    # @return [Nokogiri::HTML::Document]
    # @api private
    #
    def to_dom(options = {})
      Domize.new(self, options).call
    end

    # Return a string representation that is close to the literal form
    #
    # @example
    #   H[:p, {class: 'foo'}].inspect #=> "H[:p, {\"class\"=>\"foo\"}]"
    #
    # @return [String]
    # @api public
    #
    def inspect
      self.class.inspect_name + [tag, attributes, children].compact.reject(&:empty?).inspect
    end

    # Pretty print, a multiline representation with indentation
    #
    # @example
    #   H[:p, [[:span], [:div]]].pp # => "H[:p, [\n  H[:span],\n  H[:div]]]"
    #
    # @return [String]
    # @api public
    #
    def pp
      self.class::PP.new(self).call
    end

    # Is this a text node? Returns false
    #
    # @example
    #   H[:p].text? #=> false
    #
    # @return [FalseClass]
    # @api public
    #
    def text?
      false
    end

    def tag?(tag)
      self.tag == tag
    end

    # Return a new node, with a different tag
    #
    # @example
    #   H[:div, class: 'foo'].set_tag(:bar)
    #   # => H[:bar, class: 'foo']
    #
    # @param tag [#to_sym] The new tag
    # @return [Hexp::Node]
    #
    # @api public
    def set_tag(tag)
      H[tag.to_sym, attributes, children]
    end

    # Replace nodes in a tree
    #
    # With a CSS selector string like +"form.checkout"+ you specify the nodes
    # you want to operate on. These will be passed one by one into the block.
    # The block returns the {Hexp::Node} that will replace the old node, or it
    # can replace an +Array+ of nodes to fill the place of the old node.
    #
    # Because of this you can add one or more nodes, or remove nodes by
    # returning an empty array.
    #
    # @example Remove all script tags
    #   tree.replace('script') {|_| [] }
    #
    # @example Wrap each +<input>+ tag into a +
+ tag
    #   tree.replace('input') do |input|
    #     H[:p, input]
    #   end
    #
    # @param [String] css_selector
    #
    # @yieldparam [Hexp::Node]
    #   The matching nodes
    #
    # @return [Hexp::Node]
    #   The rewritten tree
    #
    # @api public
    def rewrite(css_selector = nil, &block)
      return Rewriter.new(self, block) if css_selector.nil?
      CssSelection.new(self, css_selector).rewrite(&block)
    end
    alias :replace :rewrite

    # Select nodes based on a css selector
    #
    # @param [String] css_selector
    # @yieldparam [Hexp::Node]
    #
    # @return [Hexp::Selector,Hexp::CssSelector]
    #
    # @api public
    def select(css_selector = nil, &block)
      if css_selector
        CssSelection.new(self, css_selector).each(&block)
      else
        Selection.new(self, block)
      end
    end

    # Run a number of processors on this node
    #
    # This is pure convenience, but it helps to conceptualize the "processor"
    # idea of a component (be it a lambda or other object), that responds to
    # call, and transform a {Hexp::Node} tree.
    #
    # @example
    #   hexp.process(
    #     ->(node) { node.replace('.section') {|node| H[:p, class: 'big', node]} },
    #     ->(node) { node.add_class 'foo' },
    #     InlineAssets.new
    #   )
    #
    # @param processors [Array<#call>]
    # @return [Hexp::Node]
    # @api public
    #
    def process(*processors)
      processors.empty? ? self : processors.first.(self).process(*processors.drop(1))
    end

    private

    # Set an attribute, used internally by #attr
    #
    # Setting an attribute to nil will delete it
    #
    # @param name [String|Symbol]
    # @param value [String|NilClass]
    # @return [Hexp::Node]
    #
    # @api private
    #
    def set_attr(name, value)
      if value.nil?
        new_attrs = {}
        attributes.each do |nam,val|
          new_attrs[nam] = val unless nam == name.to_s
        end
      else
        new_attrs = attributes.merge(name.to_s => value.to_s)
      end
      self.class.new(self.tag, new_attrs, self.children)
    end

    class << self

      # Returns the class name for use in creating inspection strings
      #
      # This will return "H" if H == Hexp::Node, or "Hexp::Node" otherwise.
      #
      # @return [String]
      # @api private
      #
      def inspect_name
        if defined?(H)
          'H'
        else
          self.name
        end
      end

    end
  end
end


require 'set'

module IronHide
  class Condition
    VALID_TYPES = {
      'equal'=> :EqualCondition,
      'not_equal'=> :NotEqualCondition
    }.freeze

    # @param params [Hash] It has a single key, which is the conditional operator
    #   type. The value is the set of conditionals that must be met.
    #
    # @example
    #   { :equal => {
    #       'resource::manager_id' => ['user::manager_id'],
    #       'user::user_role_ids' => ['8']
    #     }
    #   }
    #
    # @return [EqualCondition, NotEqualCondition]
    # @raise [IronHide::InvalidConditional] for too many keys
    #
    def self.new(params, cache = NullCache.new)
      if params.length > 1
        raise InvalidConditional, "Expected #{params} to have one key"
      end
      type, conditionals = params.first
      #=> :equal, { key: val, key: val }
      #
      # See: http://ruby-doc.org/core-1.9.3/Class.html#method-i-allocate
      klass = VALID_TYPES.fetch(type){ raise InvalidConditional, "#{type} is not valid"}
      cond  = IronHide.const_get(klass).allocate
      cond.send(:initialize, conditionals, cache)
      cond
    end

    # @param conditionals [Hash]
    # @example
    #  {
    #    'resource::manager_id' => ['user::manager_id'],
    #    'user::user_role_ids' => ['8']
    #  }
    #
    # @param [IronHide::SimpleCache, IronHide::NullCache] cache
    #
    def initialize(conditionals, cache)
      @conditionals = conditionals
      @cache        = cache
    end

    attr_reader :conditionals, :cache

    # @param user [Object]
    # @param resource [Object]
    # return [Boolean] if is met
    def met?(user, resource)
      raise NotImplementedError
    end

    protected

    EVALUATE_REGEX = /
      (
       \Auser\z|    # 'user' or 'resource'
       \Aresource\z
      )
      |             # OR
      \A\w+:{2}\w+  # "word::word"
      (:{2}\w+)*    # Followed by any number of "::word"
      \z            # End of string
      /x

    # *Safely* evaluate a conditional expression
    #
    # @note
    # This does not guarantee that conditions are correctly specified.
    # For example, 'user:::manager' will not resolve to anything, and
    # and an exception will *not* be raised. The same goes for 'user:::' and
    # 'user:id'.
    #
    # @param expressions [Array<String, Object>, String, Object] an array or
    # a single expression. This represents either an immediate value (e.g.,
    # '1', 99) or a valid expression that can be interpreted (see example)
    #
    # @example
    #   ['user::manager_id']     #=> [1]
    #   ['user::role_ids']       #=> [1,2,3,4]
    #   ['resource::manager_id'] #=> [1]
    #   [1,2,3,4]                #=> [1,2,3,4]
    #   'user::id'               #=> [1]
    #   'resource::id'           #=> [2]
    #
    # @return [Array<Object>] a collection of 0 or more objects
    # representing attributes on the user or resource
    #
    def evaluate(expression, user, resource)
      Array(expression).flat_map do |el|
        if expression?(el)
          cache.fetch(el) {
            type, *ary  = el.split('::')
            if type == 'user'
              Array(ary.inject(user) do |rval, attr|
                rval.freeze.public_send(attr)
              end)
            elsif type == 'resource'
              Array(ary.inject(resource) do |rval, attr|
                rval.freeze.public_send(attr)
              end)
            else
              raise "Expected #{type} to be 'resource' or 'user'"
            end
          }
        else
          el
        end
      end
    end

    def expression?(expression)
      !!(expression =~ EVALUATE_REGEX)
    end

    def with_error_handling
      yield
    rescue => e
      new_exception = InvalidConditional.new(e.to_s)
      new_exception.set_backtrace(e.backtrace)
      raise new_exception
    end
  end

  # @api private
  class EqualCondition < Condition
    def met?(user, resource)
      with_error_handling do
        conditionals.all? do |left, right|
          (evaluate(left, user, resource) & evaluate(right, user, resource)).size > 0
        end
      end
    end
  end

  # @api private
  class NotEqualCondition < Condition
    def met?(user, resource)
      with_error_handling do
        conditionals.all? do |left, right|
          !((evaluate(left, user, resource) & evaluate(right, user, resource)).size > 0)
        end
      end
    end
  end
end


module IronHide
  class Configuration

    attr_accessor :adapter, :namespace, :json, :memoize

    def initialize
      @adapter   = :file
      @namespace = 'com::IronHide'
      @memoize   = true
    end

    def memoizer
      memoize ? SimpleCache : NullCache
    end

    # Extend configuration variables
    #
    # @param config_hash [Hash]
    #
    # @example
    #   IronHide.configuration.add_configuration(couchdb_server: 'http://127.0.0.1:5984')
    #   IronHide.configuration.couchdb_server)
    #   #=> 'http://127.0.0.1:5984'
    #
    #   IronHide.configuration.couchdb_server = 'other'
    #   #=> 'other'
    #
    def add_configuration(config_hash)
      config_hash.each do |key, val|
        instance_eval { instance_variable_set("@#{key}",val) }
        self.class.instance_eval { attr_accessor key }
      end
    end
  end
end


require 'socket'
require 'timeout'
require_relative 'psutil_error'

module PosixPsutil
module COMMON

LINUX_PLATFORM = /linux/i
NON_LINUX_PLATFORM = /darwin|mac os|solaris|bsd|aix/i
OSX_PLAFORM = /darwin|mac os/i
NON_OSX_PLATFORM = /linux|solaris|bsd|aix/i

# The name of dynamic library where we put platform specific C extention into.
# If you want to change the name, please change ext/*/Makefile too.
EXT=File.expand_path('../../../ext', __FILE__)
LibLinuxName = File.join(EXT, 'linux', 'libposixpsutil.so')
LibPosixName = File.join(EXT, 'posix', 'libposixpsutil.so')
LibOSXName = File.join(EXT, 'posix', 'libposixpsutil.dylib')

CLOCK_TICKS = 100
PAGE_SIZE = 4096

STATUS_RUNNING = "running"
STATUS_SLEEPING = "sleeping"
STATUS_DISK_SLEEP = "disk-sleep"
STATUS_STOPPED = "stopped"
STATUS_TRACING_STOP = "tracing-stop"
STATUS_ZOMBIE = "zombie"
STATUS_DEAD = "dead"
STATUS_WAKE_KILL = "wake-kill"
STATUS_WAKING = "waking"
STATUS_IDLE = "idle"  # BSD
STATUS_LOCKED = "locked"  # BSD
STATUS_WAITING = "waiting"  # BSD

# Calculate percentage usage of 'used' against 'total'.
def self.usage_percent(used, total, _round=nil)
  begin
      ret = (used / total.to_f) * 100
  rescue ZeroDivisionError
      ret = 0
  end
  if _round
      return ret.round(_round)
  else
      return ret
  end
end

end

module NetworkConstance
  AF_INET = Socket::PF_INET
  AF_INET6 = Socket::PF_INET6
  AF_UNIX = Socket::PF_UNIX
  SOCK_STREAM = Socket::SOCK_STREAM
  SOCK_DGRAM = Socket::SOCK_DGRAM

  CONN_ESTABLISHED = "ESTABLISHED"
  CONN_SYN_SENT = "SYN_SENT"
  CONN_SYN_RECV = "SYN_RECV"
  CONN_FIN_WAIT1 = "FIN_WAIT1"
  CONN_FIN_WAIT2 = "FIN_WAIT2"
  CONN_TIME_WAIT = "TIME_WAIT"
  CONN_CLOSE = "CLOSE"
  CONN_CLOSE_WAIT = "CLOSE_WAIT"
  CONN_LAST_ACK = "LAST_ACK"
  CONN_LISTEN = "LISTEN"
  CONN_CLOSING = "CLOSING"
  CONN_NONE = "NONE"

  # http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h
  TCP_STATUSES = {
      "01"=> CONN_ESTABLISHED,
      "02"=> CONN_SYN_SENT,
      "03"=> CONN_SYN_RECV,
      "04"=> CONN_FIN_WAIT1,
      "05"=> CONN_FIN_WAIT2,
      "06"=> CONN_TIME_WAIT,
      "07"=> CONN_CLOSE,
      "08"=> CONN_CLOSE_WAIT,
      "09"=> CONN_LAST_ACK,
      "0A"=> CONN_LISTEN,
      "0B"=> CONN_CLOSING
  }

end

# this module places helper functions used in all posix platform
# Process implementions
module POSIX

# Check whether pid exists in the current process table."""
def pid_exists(pid)
  return false if pid < 0
  # According to "man 2 kill" PID 0 has a special meaning:
  # it refers to <<every process in the process group of the
  # calling process>> so we don't want to go any further.
  # If we get here it means this UNIX platform *does* have
  # a process with id 0.
  return true if pid == 0

  ::Process.kill(0, pid)
  return true
  rescue Errno::ESRCH # No such process
    return false
  rescue Errno::EPERM
    # EPERM clearly means there's a process to deny access to
    return true
  rescue RangeError # the given pid is invalid.
    return false
end
module_function :pid_exists

# Wait for process with pid 'pid' to terminate and return its
# exit status code as an integer.
#
# If pid is not a children of Process.pid (current process) just
# waits until the process disappears and return nil.
#
# If pid does not exist at all return nil immediately.
#
# Raise Timeout::Error on timeout expired.
def wait_pid(pid, timeout=nil)

  def check_timeout(delay, stop_at, timeout)
    if timeout
      raise Timeout::Error.new("when waiting for (pid=#{pid})") if Time.now >= stop_at
    end
    sleep(delay)
    delay * 2 < 0.04 ? delay * 2 : 0.04
  end

  if timeout
    waitcall = proc { ::Process.wait(pid, ::Process::WNOHANG)}
    stop_at = Time.now + timeout
  else
    waitcall = proc { ::Process.wait(pid)}
  end

  delay = 0.0001
  loop do
    begin
      retpid = waitcall.call()
    rescue Errno::EINTR
      delay = check_timeout(delay, stop_at, timeout)
      next
    rescue Errno::ECHILD
      # This has two meanings:
      # - pid is not a child of Process.pid in which case
      #   we keep polling until it's gone
      # - pid never existed in the first place
      # In both cases we'll eventually return nil as we
      # can't determine its exit status code.
      loop do
        return nil unless pid_exists(pid)
        delay = check_timeout(delay, stop_at, timeout)
      end
    end

    unless retpid
      # WNOHANG was used, pid is still running
      delay = check_timeout(delay, stop_at, timeout)
      next
    end

    # process exited due to a signal; return the integer of
    # that signal
    if $?.signaled?
      return $?.termsig
    # process exited using exit(2) system call; return the
    # integer exit(2) system call has been called with
    elsif $?.exited?
      return $?.exitstatus
    else
      # should never happen
      raise RuntimeError.new("unknown process exit status")
    end
  end
end

end
end


require 'rim/module_helper'
require 'rim/rim_info'
require 'rim/file_helper'
require 'rim/dirty_check'
require 'rim/status_builder'
require 'tempfile'

module RIM

class UploadModuleHelper < ModuleHelper

  def initialize(workspace_root, module_info, review, logger)
    super(workspace_root, module_info, logger)
    @review = review
  end

  # do the module uploads for revisions given by sha
  def upload(parent, sha1s)
    upload_module_changes(parent, sha1s)
  end

private

  # upload the content of the module
  def upload_module_changes(parent_sha1, sha1s)
    remote_path = fetch_module
    # search for the first revision that is not
    tmp_git_path = clone_or_fetch_repository(remote_path, module_tmp_git_path(@remote_path))
    RIM::git_session(tmp_git_path) do |dest|
      local_branch = nil
      remote_branch = nil
      infos = nil
      if @module_info.subdir
        dest_path = File.join([tmp_git_path] + @module_info.subdir.split("/"))
      else
        dest_path = tmp_git_path
      end
      RIM::git_session(@ws_root) do |src|
        infos = get_branches_and_revision_infos(src, dest, parent_sha1, sha1s)
        if infos.branches.size == 1
          remote_branch = infos.branches[0]
          if dest.has_remote_branch?(remote_branch)
            infos.rev_infos.each do |rev_info|
              local_branch = create_update_branch(dest, infos.parent_sha1, rev_info.src_sha1) if !local_branch
              copy_revision_files(
                src,
                rev_info.src_sha1,
                dest_path,
                rev_info.rim_info.ignores
              )
              commit_changes(dest, local_branch, rev_info.src_sha1, rev_info.message)
            end
          else
            raise RimException.new("The target revision '#{@module_info.target_revision}' of module #{@module_info.local_path} is not a branch. No push can be performed.")
          end
        elsif infos.branches.size > 1
          raise RimException.new("There are commits for module #{@module_info.local_path} on multiple target revisions (#{infos.branches.join(", ")}).")
        end
      end
      # Finally we're done. Push the changes
      if local_branch && dest.rev_sha1(local_branch) != infos.parent_sha1
        push_branch = @review && @module_info.remote_branch_format && !@module_info.remote_branch_format.empty? \
            ? @module_info.remote_branch_format % remote_branch : remote_branch
        dest.execute("git push #{@remote_url} #{local_branch}:#{push_branch}")
        dest.execute("git checkout --detach #{local_branch}")
        dest.execute("git branch -D #{local_branch}")
        @logger.info("Commited changes for module #{@module_info.local_path} to remote branch #{push_branch}.")
      else
        @logger.info("No changes to module #{@module_info.local_path}.")
      end
    end
  end

  # search backwards for all revision infos
  def get_branches_and_revision_infos(src_session, dest_session, parent_sha1, sha1s)
    infos = []
    branches = []
    dest_parent_sha1 = nil
    (sha1s.size() - 1).step(0, -1) do |i|
      info = get_revision_info(src_session, dest_session, sha1s[i])
      if !info.dest_sha1 && info.rim_info.target_revision
        infos.unshift(info)
        branches.push(info.rim_info.target_revision) if !branches.include?(info.rim_info.target_revision)
      else
        dest_parent_sha1 = info.dest_sha1
        break
      end
    end
    dest_parent_sha1 = get_riminfo_for_revision(src_session, parent_sha1).revision_sha1 if !dest_parent_sha1
    dest_parent_sha1 = infos.first.rim_info.revision_sha1 if !dest_parent_sha1 && !infos.empty?
    return Struct.new(:branches, :parent_sha1, :rev_infos).new(branches, dest_parent_sha1, infos)
  end

  RevisionInfo = Struct.new(:dest_sha1, :src_sha1, :rim_info, :message)

  # collect infos for a revision
  def get_revision_info(src_session, dest_session, src_sha1)
    module_status = StatusBuilder.new.rev_module_status(src_session, src_sha1, @module_info.local_path)
    rim_info = get_riminfo_for_revision(src_session, src_sha1)
    dest_sha1 = dest_session.rev_sha1("rim-#{src_sha1}")
    msg = src_session.execute("git show -s --format=%B #{src_sha1}")
    RevisionInfo.new(module_status && module_status.dirty? ? dest_sha1 : rim_info.revision_sha1, src_sha1, rim_info, msg)
  end

  # commit changes to session
  def commit_changes(session, branch, sha1, msg)
    if session.status.lines.any?
      # add before commit because the path can be below a not yet added path
      session.execute("git add --all")
      msg_file = Tempfile.new('message')
      begin
        msg_file << msg
        msg_file.close
        session.execute("git commit -F #{msg_file.path}")
      ensure
        msg_file.close(true)
      end
      # create tag
      session.execute("git tag rim-#{sha1} refs/heads/#{branch}")
    end
  end

  # get target revision for this module for workspace revision
  def get_riminfo_for_revision(session, sha1)
    session.execute("git show #{sha1}:#{File.join(@module_info.local_path, RimInfo::InfoFileName)}") do |out, e|
      return RimInfo.from_s(!e ? out : "")
    end
  end

  # create update branch for given revision
  def create_update_branch(session, dest_sha1, src_sha1)
    branch = "rim/#{src_sha1}"
    session.execute("git checkout -B #{branch} #{dest_sha1}")
    branch
  end

  # copy files from given source revision into destination dir
  def copy_revision_files(src_session, src_sha1, dest_dir, ignores)
    Dir.mktmpdir do |tmp_dir|
      tmp_dir = Dir.glob(tmp_dir)[0]
      src_session.execute("git archive --format tar #{src_sha1} #{@module_info.local_path} | tar -C #{tmp_dir} -xf -")
      tmp_module_dir = File.join(tmp_dir, @module_info.local_path)
      files = FileHelper.find_matching_files(tmp_module_dir, false, "/**/*", File::FNM_DOTMATCH)
      files.delete(".")
      files.delete("..")
      files.delete(RimInfo::InfoFileName)
      files -= FileHelper.find_matching_files(tmp_module_dir, false, ignores)
      # have source files now. Now clear destination folder and copy
      prepare_empty_folder(dest_dir, ".git/**/*")
      files.each do |f|
        src_path = File.join(tmp_module_dir, f)
        if File.file?(src_path)
          path = File.join(dest_dir, f)
          FileUtils.mkdir_p(File.dirname(path))
          FileUtils.cp(src_path, path)
        end
      end
    end
  end

end

end