# File lib/asciidoctor/substituters.rb, line 276
  def sub_attributes(data)
    return data if data.nil? || data.empty?

    string_data = data.is_a? String
    # normalizes data type to an array (string becomes single-element array)
    lines = string_data ? [data] : data

    result = []
    lines.each {|line|
      reject = false
      line = line.gsub(REGEXP[:attr_ref]) {
        # alias match for Ruby 1.8.7 compat
        m = $~
        # escaped attribute, return unescaped
        if !m[1].nil? || !m[4].nil?
          "{#{m[2]}}"
        elsif (directive = m[3])
          offset = directive.length + 1
          expr = m[2][offset..-1]
          case directive
          when 'set'
            args = expr.split(':')
            _, value = Lexer::store_attribute(args[0], args[1] || '', @document)
            if value.nil?
              reject = true
              break '{undefined}'
            end
            ''
          when 'counter', 'counter2'
            args = expr.split(':')
            val = @document.counter(args[0], args[1])
            directive == 'counter2' ? '' : val
          else
            # if we get here, our attr_ref regex is too loose
            puts "asciidoctor: WARNING: illegal attribute directive: #{m[2]}"
            ''
          end
        elsif (key = m[2].downcase) && @document.attributes.has_key?(key)
          @document.attributes[key]
        elsif INTRINSICS.has_key? key
          INTRINSICS[key]
        else
          Debug.debug { "Missing attribute: #{key}, line marked for removal" }
          reject = true
          break '{undefined}'
        end
      } if line.include? '{' 

      result << line unless reject
    }

    string_data ? result.join : result
  end