def match(conditions)
      conditions = validate_conditions(conditions)

      # check content of child nodes
      if conditions[:content]
        if children.empty?
          return false unless match_condition("", conditions[:content])
        else
          return false unless children.find { |child| child.match(conditions[:content]) }
        end
      end

      # test the name
      return false unless match_condition(@name, conditions[:tag]) if conditions[:tag]

      # test attributes
      (conditions[:attributes] || {}).each do |key, value|
        return false unless match_condition(self[key], value)
      end

      # test parent
      return false unless parent.match(conditions[:parent]) if conditions[:parent]

      # test children
      return false unless children.find { |child| child.match(conditions[:child]) } if conditions[:child]
   
      # test ancestors
      if conditions[:ancestor]
        return false unless catch :found do
          p = self
          throw :found, true if p.match(conditions[:ancestor]) while p = p.parent
        end
      end

      # test descendants
      if conditions[:descendant]
        return false unless children.find do |child|
          # test the child
          child.match(conditions[:descendant]) ||
          # test the child's descendants
          child.match(:descendant => conditions[:descendant])
        end
      end
      
      # count children
      if opts = conditions[:children]
        matches = children.select do |c|
          c.match(/./) or
          (c.kind_of?(HTML::Tag) and (c.closing == :self or ! c.childless?))
        end
        
        matches = matches.select { |c| c.match(opts[:only]) } if opts[:only]
        opts.each do |key, value|
          next if key == :only
          case key
            when :count
              if Integer === value
                return false if matches.length != value
              else
                return false unless value.include?(matches.length)
              end
            when :less_than
              return false unless matches.length < value
            when :greater_than
              return false unless matches.length > value
            else raise "unknown count condition #{key}"
          end
        end
      end

      # test siblings
      if conditions[:sibling] || conditions[:before] || conditions[:after]
        siblings = parent ? parent.children : []
        self_index = siblings.index(self)

        if conditions[:sibling]
          return false unless siblings.detect do |s| 
            s != self && s.match(conditions[:sibling])
          end
        end

        if conditions[:before]
          return false unless siblings[self_index+1..-1].detect do |s| 
            s != self && s.match(conditions[:before])
          end
        end

        if conditions[:after]
          return false unless siblings[0,self_index].detect do |s| 
            s != self && s.match(conditions[:after])
          end
        end
      end
  
      true
    end
def match(conditions)
      conditions = validate_conditions(conditions)

      # check content of child nodes
      if conditions[:content]
        if children.empty?
          return false unless match_condition("", conditions[:content])
        else
          return false unless children.find { |child| child.match(conditions[:content]) }
        end
      end

      # test the name
      return false unless match_condition(@name, conditions[:tag]) if conditions[:tag]

      # test attributes
      (conditions[:attributes] || {}).each do |key, value|
        return false unless match_condition(self[key], value)
      end

      # test parent
      return false unless parent.match(conditions[:parent]) if conditions[:parent]

      # test children
      return false unless children.find { |child| child.match(conditions[:child]) } if conditions[:child]
   
      # test ancestors
      if conditions[:ancestor]
        return false unless catch :found do
          p = self
          throw :found, true if p.match(conditions[:ancestor]) while p = p.parent
        end
      end

      # test descendants
      if conditions[:descendant]
        return false unless children.find do |child|
          # test the child
          child.match(conditions[:descendant]) ||
          # test the child's descendants
          child.match(:descendant => conditions[:descendant])
        end
      end
      
      # count children
      if opts = conditions[:children]
        matches = children.select do |c|
          c.match(/./) or
          (c.kind_of?(HTML::Tag) and (c.closing == :self or ! c.childless?))
        end
        
        matches = matches.select { |c| c.match(opts[:only]) } if opts[:only]
        opts.each do |key, value|
          next if key == :only
          case key
            when :count
              if Integer === value
                return false if matches.length != value
              else
                return false unless value.include?(matches.length)
              end
            when :less_than
              return false unless matches.length < value
            when :greater_than
              return false unless matches.length > value
            else raise "unknown count condition #{key}"
          end
        end
      end

      # test siblings
      if conditions[:sibling] || conditions[:before] || conditions[:after]
        siblings = parent ? parent.children : []
        self_index = siblings.index(self)

        if conditions[:sibling]
          return false unless siblings.detect do |s| 
            s != self && s.match(conditions[:sibling])
          end
        end

        if conditions[:before]
          return false unless siblings[self_index+1..-1].detect do |s| 
            s != self && s.match(conditions[:before])
          end
        end

        if conditions[:after]
          return false unless siblings[0,self_index].detect do |s| 
            s != self && s.match(conditions[:after])
          end
        end
      end
  
      true
    end
Returns +true+ if the node meets any of the given conditions. The
 +conditions+ parameter must be a hash of any of the following keys
 (all are optional):

 * <tt>:tag</tt>: the node name must match the corresponding value
 * <tt>:attributes</tt>: a hash. The node's values must match the
   corresponding values in the hash.
 * <tt>:parent</tt>: a hash. The node's parent must match the
   corresponding hash.
 * <tt>:child</tt>: a hash. At least one of the node's immediate children
   must meet the criteria described by the hash.
 * <tt>:ancestor</tt>: a hash. At least one of the node's ancestors must
   meet the criteria described by the hash.
 * <tt>:descendant</tt>: a hash. At least one of the node's descendants
   must meet the criteria described by the hash.
 * <tt>:sibling</tt>: a hash. At least one of the node's siblings must
   meet the criteria described by the hash.
 * <tt>:after</tt>: a hash. The node must be after any sibling meeting
   the criteria described by the hash, and at least one sibling must match.
 * <tt>:before</tt>: a hash. The node must be before any sibling meeting
   the criteria described by the hash, and at least one sibling must match.
 * <tt>:children</tt>: a hash, for counting children of a node. Accepts the
   keys:
 ** <tt>:count</tt>: either a number or a range which must equal (or
    include) the number of children that match.
 ** <tt>:less_than</tt>: the number of matching children must be less than
    this number.
 ** <tt>:greater_than</tt>: the number of matching children must be
    greater than this number.
 ** <tt>:only</tt>: another hash consisting of the keys to use
    to match on the children, and only matching children will be
    counted.

 Conditions are matched using the following algorithm:

 * if the condition is a string, it must be a substring of the value.
 * if the condition is a regexp, it must match the value.
 * if the condition is a number, the value must match number.to_s.
 * if the condition is +true+, the value must not be +nil+.
 * if the condition is +false+ or +nil+, the value must be +nil+.

 Usage:

   # test if the node is a "span" tag
   node.match :tag => "span"

   # test if the node's parent is a "div"
   node.match :parent => { :tag => "div" }

   # test if any of the node's ancestors are "table" tags
   node.match :ancestor => { :tag => "table" }

   # test if any of the node's immediate children are "em" tags
   node.match :child => { :tag => "em" }

   # test if any of the node's descendants are "strong" tags
   node.match :descendant => { :tag => "strong" }

   # test if the node has between 2 and 4 span tags as immediate children
   node.match :children => { :count => 2..4, :only => { :tag => "span" } }

   # get funky: test to see if the node is a "div", has a "ul" ancestor
   # and an "li" parent (with "class" = "enum"), and whether or not it has
   # a "span" descendant that contains # text matching /hello world/:
   node.match :tag => "div",
              :ancestor => { :tag => "ul" },
              :parent => { :tag => "li",
                           :attributes => { :class => "enum" } },
              :descendant => { :tag => "span",
                               :child => /hello world/ }