class Rexslt

Public Class Methods

new(xsl, xml, raw_params={}, debug: false) click to toggle source
Calls superclass method
# File lib/rexslt.rb, line 68
def initialize(xsl, xml, raw_params={}, debug: false)

  ## debugging variables

  @rn = 0
  @rre = 0

  super()
  puts 'before options'.info if @debug
  @options = {}

  params = raw_params.merge({debug: false})
  @debug = debug
  custom_params = params.inject({}){|r,x| r.merge(Hash[x[0].to_s,x[1]])}
  puts 'before xsl_transform'.info if @debug

  xslt_transform(*[xsl, xml].map{|x| RXFReader.read(x).first}, custom_params)
end

Public Instance Methods

to_doc() click to toggle source
# File lib/rexslt.rb, line 91
def to_doc(); @doc; end
to_s(options={}) click to toggle source
# File lib/rexslt.rb, line 87
def to_s(options={})
  @doc.to_s(@options.merge(options)).sub(/<root4>/,'').sub(/<\/root4>$/m,'').lstrip
end
to_xml() click to toggle source
# File lib/rexslt.rb, line 93
def to_xml()
  @doc.root.xml(pretty: true).sub(/<root4>/,'').sub(/<\/root4>$/m,'')
end

Private Instance Methods

filter_out_spaces(e) click to toggle source
# File lib/rexslt.rb, line 99
def filter_out_spaces(e)

  e.children.each do |x|

    if x.is_a? String and e.name != 'xsl:text' then

      x.gsub!(/\n/,'')
      x.gsub!(/ +/,'')

    elsif x.is_a? Rexle::Element and x.children.length > 0 then
      filter_out_spaces x
    end

  end
end
ignore(*args) click to toggle source

Ignores comment tags

# File lib/rexslt.rb, line 364
def ignore(*args)
end
indent_after(element, x, doc_element, prev_indent) click to toggle source
# File lib/rexslt.rb, line 380
def indent_after(element, x, doc_element, prev_indent)

  puts 'indent? : ' + @indent.inspect if @debug

  if @indent == true then
    doc_element.add_text  '  ' * (prev_indent > 0 ? prev_indent - 1 : prev_indent)
  end
end
indent_before(element, x, doc_element, indent, i) click to toggle source
# File lib/rexslt.rb, line 367
def indent_before(element, x, doc_element, indent, i)
  text = ''
  unless doc_element.texts.empty? and doc_element.texts.last.nil? then
    if indent > 1 then
      text = "\n" + '  ' * (indent - 1)  #unless doc_element.texts.last.to_s[/^\n\s/m]
    end
  else
    text = "\n" + '  ' * (indent - 1)
  end

  doc_element.add_text text if text
end
indent_element(element, x, doc_element, indent, previous_indent) { || ... } click to toggle source
# File lib/rexslt.rb, line 389
def indent_element(element, x, doc_element, indent, previous_indent)
  indent_before(element, x, doc_element, indent, i) if @indent == true
  yield
  indent_after(element, x, doc_element, previous_indent) if @indent == true
end
match?(raw_keypath, raw_path) click to toggle source
# File lib/rexslt.rb, line 185
def match?(raw_keypath, raw_path)
  return false if raw_path == ['*']
  keypath = raw_keypath.split('/').reverse.take raw_path.length
  path = raw_path.reverse
  r = path.map.with_index.select{|x,i|x == '*'}.map(&:last)

  r.each do |n|
    # get the relative value from path
    path[n] = keypath[n]
  end

  keypath == path
end
padding(element,raw_indent, x) click to toggle source
# File lib/rexslt.rb, line 395
def padding(element,raw_indent, x)
  # if there is any other elements in doc_element don't check for an indent!!!!

  indent = 0
  indent = raw_indent + 1  if element.texts.length <= 0
  x.to_s.strip.length > 0 ? '  ' * indent : ''
end
position() click to toggle source
# File lib/rexslt.rb, line 199
def position()
  @position ||= 0
end
read_node(template_node, element, doc_element, indent, i=0) click to toggle source

Reads an XSL node which is either an XSL element, a string or a comment template_node: XSL node element: XML node doc_element: target document element

# File lib/rexslt.rb, line 409
def read_node(template_node, element, doc_element, indent, i=0)

  puts 'children: ' + template_node.children.inspect if @debug
  template_node.children.each_with_index do |x,j|

    puts ('x: '  + x.inspect).debug if @debug
    name = if x.kind_of? Rexle::Element then :read_raw_element
    elsif x.is_a? String then :read_raw_text
    elsif x.class.to_s =~  /Rexle::Comment$/ then :ignore
    else
      :ignore
    end
    puts ('name: ' + name.inspect).debug if @debug

    r2 = method(name).call(element, x, doc_element, indent, i)
    puts 'r2b: ' + r2.inspect if @debug
    r2

  end

end
read_raw_element(element, x, doc_element, indent, j) click to toggle source

element: xml element x: xsl element doc_element:

# File lib/rexslt.rb, line 449
def read_raw_element(element, x, doc_element, indent, j)

  method_name = x.name.gsub(/[:-]/,'_').to_sym
  puts ('method_name: ' + method_name.inspect).debug if @debug

  if @xsl_methods.include? method_name then

    if method_name == :'xsl_apply_templates' then
      #doc_element = doc_element.elements.last
    end

    method(method_name).call(element, x, doc_element, indent, j)

  else

    previous_indent = indent
    la = x.name
    new_indent = indent + 1  if @indent == true

    new_element = x.clone

    new_element.attributes.each do |k,raw_v|

      v = raw_v.is_a?(Array) ? raw_v.join(' ') : raw_v

      puts 'v: ' + v.inspect if @debug

      if v[/{/] then

        v.gsub!(/(\{[^\}]+\})/) do |x2|

          xpath = x2[/\{([^\}]+)\}/,1]
          puts 'element.text(xpath): ' + element.text(xpath).inspect if @debug
          text = element.text(xpath).to_s.strip
          puts 'text: ' + text.inspect if @debug
          text ? text.clone : ''

        end

        puts '2. v: ' + v.inspect if @debug

      end
    end

    puts 'x.children.length: ' + x.children.length.inspect if @debug

    if x.children.length > 0 then

      indent_before(element, x, doc_element, new_indent, j) if @indent == true

      read_node(x, element, new_element, new_indent, j)
      doc_element.add new_element

      if @indent == true then
        if doc_element.children.last.children.any? \
            {|x| x.is_a? Rexle::Element} then

          doc_element.children.last.add_text "\n" + '  ' * (new_indent - 1)
        end
      end


    else

      indent_before(element, x, new_element, new_indent, j) if @indent == true

      val = @indent == true ? x.to_s : x.to_s
      #jr020219 doc_element.add val
      doc_element.add new_element

    end

  end
  #new_element
  #puts 'attributes: ' + new_element.attributes.inspect if @debug

end
read_raw_text(element, x, doc_element, raw_indent, i) click to toggle source

element: xml source element, x: xsl element, doc_element: current destination xml element

# File lib/rexslt.rb, line 433
def read_raw_text(element, x, doc_element, raw_indent, i)

  #val = @indent == true ? padding(doc_element, raw_indent, x) : ''
  if x.to_s.strip.length > 0 then

    val = x.to_s.strip #
    puts ('val: ' + val.inspect).debug if @debug
    doc_element.add_element x.to_s
  end

end
value_of(x, elementx, i) click to toggle source
# File lib/rexslt.rb, line 527
def value_of(x, elementx, i)

  puts 'value_of: ' + elementx.to_s.inspect if @debug

  field = x.attributes[:select]

  o = case field
    when '.'
      elementx.value
    when /^\$/
      @param[field[/^\$(.*)/,1]]
    when 'position()'
      i.to_s
  else
    r = elementx.element(field)
    if r.is_a? Rexle::Element::Attribute
      r.value.to_s
    elsif r.is_a? Rexle::Element
      r.texts.join
    else
      ''
    end

  end

end
xsl_apply_templates(element, x, doc_element, indent, i) click to toggle source
# File lib/rexslt.rb, line 116
def xsl_apply_templates(element, x, doc_element, indent, i)

  field = x.attributes[:select]
  node = element.element field
  return unless node

  keypath = node.to_xpath :no_cond
  matched_node = nil

  # check for a nest <xsl:sort element

  sort = x.element('xsl:sort')

  if sort then

    orderx = sort.attributes[:order] || 'ascending'
    sort_field = sort.attributes[:select]
    data_type = sort.attributes[:'data-type'] || 'text'

  end

  raw_template = @templates.to_a.find do |raw_item, template|
    next unless raw_item
    item = raw_item.split('/')

    if match? keypath, item then
      matched_node = element.xpath field

      true
    else
      child = item.pop
      if item.length > 0 and match? keypath, item then
        matched_node = node.xpath child
        matched_node.any? ? true : false
      end
    end
  end

  if matched_node then

    template_xpath, template = raw_template

    if sort_field then

      sort_order = lambda do |x|

        r = x.element(sort_field);

        if r.respond_to?(:value) then
          r.value
        else
          data_type == 'text' ? r : r.to_i
        end
      end

      a = matched_node.sort_by(&sort_order).each_with_index do |child_node,i|
        read_node template, child_node, doc_element, indent, i+1
      end
      a.reverse! if orderx == 'descending'
    else
      r = matched_node.each_with_index do |child_node,i|
        read_node template, child_node, doc_element, indent, i+1
      end
      return r
    end
  end

end
xsl_attribute(element, x, doc_element, indent, i) click to toggle source
# File lib/rexslt.rb, line 203
def xsl_attribute(element, x, doc_element, indent, i)

  name = x.attributes[:name]
  value = x.value

  e = x.element('xsl:value-of')
  value = value_of(e, element, i) if e

  av = x.element('xsl:text')
  if av then
    value = av.text
  end

  doc_element.add_attribute(name, value)
end
xsl_call_template(element, x, doc_element, indent, i) click to toggle source
# File lib/rexslt.rb, line 219
def xsl_call_template(element, x, doc_element, indent, i)

  name = x.attributes[:name]
  template = @doc_xsl.root.element("xsl:template[@name='#{name}']")

  read_node template, element, doc_element, indent, i
end
xsl_cdata(element, x, doc_element, indent, i) click to toggle source
# File lib/rexslt.rb, line 251
def xsl_cdata(element, x, doc_element, indent, i)
  puts ('cdata x: ' + element.inspect) if @debug

  new_element = Rexle::CData.new(x.value.to_s)

  read_node(x, element, new_element, indent, i)
  doc_element.add new_element
end
xsl_choose(element, x, doc_element, indent, i) click to toggle source
# File lib/rexslt.rb, line 227
def xsl_choose(element, x, doc_element, indent, i)


  r = x.xpath("xsl:when").map do |xsl_node|

    condition = xsl_node.attributes[:test]

    if element.xpath(condition).all? then
      read_raw_element(element, xsl_node.elements.first,  doc_element, indent, i)
      true
    else
      false
    end
  end

  unless r.any? then
    otherwise = x.element("xsl:otherwise")
    if otherwise then
      read_node(otherwise, element, doc_element, indent)
    end
  end

end
xsl_comment(element, x, doc_element, indent, i) click to toggle source
# File lib/rexslt.rb, line 260
def xsl_comment(element, x, doc_element, indent, i)
  #puts ('comment x: ' + element.inspect) if @debug

  new_element = Rexle::Comment.new(x.value.to_s)

  read_node(x, element, new_element, indent, i)
  doc_element.add new_element
end
xsl_copy_of(element, x, doc_element, indent, i) click to toggle source
# File lib/rexslt.rb, line 269
def xsl_copy_of(element, x, doc_element, indent, i)
  #jr251012 indent = 1 unless indent
  #jr251012 indent_element(element, x, doc_element, indent, indent - 1) do
    field = x.attributes[:select]
    element.xpath(field).each do |child|
      doc_element.add child
    end
  #jr251012 end

end
xsl_element(element, x, doc_element, indent, i) click to toggle source
# File lib/rexslt.rb, line 280
def xsl_element(element, x, doc_element, indent, i)

  indent_before(element, x, doc_element, indent + 1, i) if @indent == true

  name = x.attributes[:name]
  variable = name[/^\{(.*)\}$/,1]

  puts 'variable: ' + variable.inspect if @debug

  if variable then
    name = element.element(variable)
  end

  new_element = Rexle::Element.new(name) # .add_text(x.value.strip)
  puts 'element.text: ' + element.to_s.inspect if @debug
  new_element.text = element.text.to_s.strip

  read_node(x, element, new_element, indent, i)
  doc_element.add new_element
  indent_after(element, x, doc_element, indent) if @indent == true
end
xsl_for_each(element, x, doc_element, indent, i) click to toggle source
# File lib/rexslt.rb, line 302
def xsl_for_each(element, x, doc_element, indent, i)

  puts ('inside xsl_for_each x.children: ' + x.children.inspect).debug if @debug
  xpath = x.attributes[:select]

  nodes = element.xpath xpath

  # check for sort
  sort_node = x.element 'xsl:sort'

  if sort_node then

    sort_field = sort_node.attributes[:select]
    raw_order = sort_node.attributes[:order]
    sort_node.parent.delete sort_node

    if sort_field then

      nodes = nodes.sort_by do |node|

        r = node.element sort_field

        if r.is_a? Rexle::Element or r.is_a? Rexle::Element::Attribute then
          r.value
        else
          # it's a string
          r
        end
      end

    end

    field = raw_order[/^\{\$(.*)\}/,1]
    order =  field ?  @param[field] : raw_order
    nodes.reverse! if order.downcase == 'descending'
  end
  puts ('nodes: ' + nodes.inspect).debug if @debug
  nodes.each.with_index {|node, j| read_node(x, node, doc_element, indent, j+1)}

end
xsl_if(element, x, doc_element, indent, i=0) click to toggle source
# File lib/rexslt.rb, line 343
def xsl_if(element, x, doc_element, indent, i=0)

  cond = x.attributes[:test].clone
  puts ('cond: ' + cond.inspect).debug if @debug

  cond.sub!(/position\(\)/, i.to_s)
  cond.sub!(/&lt;/,'<')
  cond.sub!(/&gt;/,'>')
  cond.sub!(/\bmod\b/,'%')
  cond.gsub!(/&apos;/,"'")

  result = element.element cond

  if result then
    read_node x, element,  doc_element, indent, i
  end

end
xsl_output() click to toggle source
# File lib/rexslt.rb, line 554
def xsl_output()

end
xsl_text(element, x, doc_element, indent, i) click to toggle source
# File lib/rexslt.rb, line 558
def xsl_text(element, x, doc_element, indent, i)

  puts ('inside xsl_text x.value:' + x.inspect).debug if @debug
  val = @indent == true ? padding(doc_element, indent, x) : ''

  val += if x.attributes[:"disable-output-escaping"] then
    x.value.unescape
  else
    x.value.to_s
  end

  doc_element.add_element val

end
xsl_value_of(element, x, doc_element, indent, i) click to toggle source
# File lib/rexslt.rb, line 573
def xsl_value_of(element, x, doc_element, indent, i)

  #puts 'xsl_value_of: ' + x.inspect if @debug
  s = value_of(x, element,i)
  puts ('xsl_value_of s: ' + s.inspect).debug if @debug

  doc_element.add_text  s
  doc_element

end
xslt_transform(raw_xsl, xml, custom_params={}) click to toggle source
# File lib/rexslt.rb, line 585
def xslt_transform(raw_xsl, xml, custom_params={})

  puts 'inside xslt_transform'.info if @debug

  doc_xml = xml.is_a?(Rexle) ? xml : Rexle.new(xml)

  @doc_xsl = raw_xsl.is_a?(Rexle) ? raw_xsl : Rexle.new(raw_xsl.gsub(/(?<=\<\/xsl:text>)[^<]+/,''))
  puts 'after @doc_xsl'.info if @debug

  #jr2040516 filter_out_spaces @doc_xsl.root

  @doc = Rexle.new '<root4></root4>', debug: @debug

  indent = 0

  previous_indent = 0
  @xsl_methods = %i(apply_templates value_of element if choose when copy_of
                    attribute for_each text output call_template comment cdata).map do |x|
                      ('xsl_' + x.to_s).to_sym
                    end

  strip_space = @doc_xsl.root.element "xsl:strip-space/attribute::elements"

  if strip_space then
    elements = strip_space.value
    elements.split.each do |element|
      nodes = doc_xml.root.xpath "//" + element + "[text()]"
      a = nodes.select {|x| x.value.to_s.strip.empty?}
      a.each {|node| node.parent.delete node}
    end
  end

  h = @doc_xsl.root.element("xsl:output/attribute::*")
  puts 'h: ' + h.inspect if @debug
  puts 'after h'.info if @debug

  if  h and ((h[:method] and h[:method].downcase == 'html') \
                                 or h[:'omit-xml-declaration'] == 'yes') then
    @options[:declaration] = :none
  end

  @indent =  (h and h[:indent] == 'yes') ? true : false

  params = @doc_xsl.root.xpath("xsl:param").map{|x| [x.attributes[:name], x.value]}
  @param = Hash[params].merge(custom_params) if params
  # search for params


  # fetch the templates
  #puts "Rexle:Version: " + Rexle.version

  @templates = @doc_xsl.root.xpath('xsl:template').inject({}) do |r,x|
    r.merge(x.attributes[:match] || x.attributes[:select] => x)
  end

  # using the 1st template
  xpath = String.new @templates.to_a[0][0]
  out = read_node(@templates.to_a[0][-1], doc_xml.element(xpath), @doc.root, indent)

  puts ('out: ' + out.inspect).debug if @debug

  html = @doc.root.element('html')

  if html then

      if h and h[:'omit-xml-declaration'] != 'yes'  then
      else
        @options[:declaration] = :none
      end

  end

  if @doc_xsl.root.element('xsl:output[@method="html"]') or html then

    head = @doc.root.element('html/head')

    if head and not head.element('meta[@content]') then

      h = {
        :'http-equiv' => "Content-Type",
        content: 'text/html; charset=utf-8'
      }
      meta_element = Rexle::Element.new('meta', attributes: h)
      child = head.element('*')

      if child then
        child.insert_before meta_element
      else
        head.add meta_element
      end

    end
  end

  out

end