Class SDL4R::Tag
In: lib/sdl4r/tag.rb
Parent: Object

SDL documents are made of Tags.

See the README for a longer explanation on SDL documents.

Do not assume that methods returning sets (Hash, Array, etc) of children/values/attributes/etc in this class returns copies or implementations. It can be one or the other depending on the method. The implementations are designed to be correct and somewhat efficient, not too protect the Tag internal state from ill-use of the returned values.

Authors

Daniel Leuck, Philippe Vosges

Methods

Attributes

name  [R]  the name of this Tag
namespace  [R]  the namespace of this Tag or an empty string when there is no namespace (i.e. default namespace).

Public Class methods

Creates an empty tag in the given namespace. If the namespace is nil it will be coerced to an empty String.

  tag = Tag.new("name")
  tag = Tag.new("namespace", "name")

  tag = Tag.new("fruit") do
    add_value 2
    new_child("orange") do
      set_attribute("quantity", 2)
    end
  end

which builds the following SDL structure

  fruit 2 {
    orange quantity=2
  }

Raises

ArgumentError if the name is not a legal SDL identifier (see SDL4R#validate_identifier) or the namespace is non-blank and is not a legal SDL identifier.

[Source]

# File lib/sdl4r/tag.rb, line 95
    def initialize(namespace, name = nil, &block)
      namespace, name = to_nns namespace, name

      raise ArgumentError, "tag namespace must be a String" unless namespace.is_a? String
      raise ArgumentError, "tag name must be a String" unless name.is_a? String

      SDL4R.validate_identifier(namespace) unless namespace.empty?
      @namespace = namespace
      
      name = name.to_s.strip
      raise ArgumentError, "Tag name cannot be nil or empty" if name.empty?
      SDL4R.validate_identifier(name)
      @name = name
      
      @children = []
      @values = []
      
      # a Hash of Hash : {namespace => {name => value}}
      # The default namespace is represented by an empty string.
      @attributesByNamespace = {}

      instance_eval(&block) if block_given?
    end

Public Instance methods

Adds the given object as a child if it is a Tag, as an attribute if it is a Hash {key => value} (supports namespaces), or as a value otherwise. If it is an array, each of its elements is added to this Tag via this operator. If any of its elements is itself an array, then an anonymous tag is created and the array is passed to it via this operator (see the examples below)

  tag << Tag.new("child")
  tag << 123                          # new integer value
  tag << "islamabad"                  # new string value
  tag << { "metric:length" => 1027 }  # new attribute (with namespace)
  tag << [nil, 456, "abc"]            # several values added

  tag = Tag.new("tag")
  tag << [[1, 2, 3], [4, 5, 6]]       # tag {
                                      #   1 2 3
                                      #   4 5 6
                                      # }

Returns self.

Use other accessors for a stricter and less "magical" behavior.

[Source]

# File lib/sdl4r/tag.rb, line 169
    def <<(o)
      if o.is_a?(Tag)
        add_child(o)
      elsif o.is_a?(Hash)
        o.each_pair { |key, value|
          namespace, key = key.split(/:/) if key.match(/:/)
          namespace ||= ""
          set_attribute(namespace, key, value)
        }
      elsif o.is_a? Array
        o.each { |item|
          if item.is_a? Array
            anonymous = new_child("content")
            anonymous << item
          else
            self << item
          end
        }
      else
        add_value(o)
      end
      return self
    end
==(o)

Alias for eql?

Add a child to this Tag.

child:The child to add

Returns the added child.

[Source]

# File lib/sdl4r/tag.rb, line 142
    def add_child(child)
      @children.push(child)
      return child
    end

Adds a value to this Tag. See SDL4R#coerce_or_fail to know about the allowable types.

v:The value to add

Raises an ArgumentError if the value is not a legal SDL type

[Source]

# File lib/sdl4r/tag.rb, line 409
    def add_value(v)
      @values.push(SDL4R::coerce_or_fail(v))
      return nil
    end
  attribute(key)
  attribute(namespace, key)

Returns the attribute of the specified namespace of specified key or nil if not found.

[Source]

# File lib/sdl4r/tag.rb, line 518
    def attribute(namespace, key = nil)
      namespace, key = to_nns namespace, key
      attributes = @attributesByNamespace[namespace]
      return attributes.nil? ? nil : attributes[key]
    end

Returns a Hash of the attributes of the specified namespace (default is all) or enumerates them.

  tag.attributes # => { "length" => 123, "width" = 25.4, "orig:color" => "gray" }
  tag.attributes("orig") do |namespace, key, value|
    p "#{namespace}:#{key} = #{value}"
  end

namespace:: namespace of the returned attributes. If nil, all attributes are returned with qualified names (e.g. "meat:color"). If "", attributes of the default namespace are returned.

[Source]

# File lib/sdl4r/tag.rb, line 556
    def attributes(namespace = nil, &block) # :yields: namespace, key, value
      if block_given?
        each_attribute(namespace, &block)
        
      else
        if namespace.nil?
          hash = {}

          each_attribute do | namespace, key, value |
            qualified_name = namespace.empty? ? key : namespace + ':' + key
            hash[qualified_name] = value
          end

          return hash

        else
          return @attributesByNamespace[namespace]
        end
      end
    end

Sets all the attributes of the default namespace for this Tag in one operation.

See set_attributes

[Source]

# File lib/sdl4r/tag.rb, line 660
    def attributes=(attribute_hash)
      set_attributes(attribute_hash)
    end
  child
  child(name)
  child(recursive, name)

Get the first child with the given name, optionally using a recursive search.

name:the name of the child Tag. If nil, the first child is returned (nil if there are

no children at all).

Returns the first child tag having the given name or nil if no such child exists

[Source]

# File lib/sdl4r/tag.rb, line 313
    def child(recursive = false, name = nil)
      if name.nil?
        name = recursive
        recursive = false
      end
      
      unless name
        return @children.first
      else
        each_child(recursive, name) { |child| return child }
      end
    end

Returns the number of children Tag.

[Source]

# File lib/sdl4r/tag.rb, line 234
    def child_count
      @children.size
    end
  children(recursive)
  children(recursive, name)
  children(recursive, namespace, name)

  children(recursive) { |child| ... }
  children(recursive, name) { |child| ... }
  children(recursive, namespace, name) { |child| ... }

Returns an Array of the children Tags of this Tag or enumerates them.

recursive:if true children and all descendants will be returned. False by default.
name:if not nil, only children having this name will be returned. Nil by default.
namespace:use nil for all namespaces and "" for the default one. Nil by default.
  tag.children # => array of the children
  tag.children(true) { |descendant| ... }

  tag.children(false, "name") # => children of name "name"
  tag.children(false, "ns", nil) # => children of namespace "ns"

[Source]

# File lib/sdl4r/tag.rb, line 258
    def children(recursive = false, namespace = nil, name = :DEFAULT, &block) # :yields: child
      if name == :DEFAULT
        name = namespace
        namespace = nil
      end

      if block_given?
        each_child(recursive, namespace, name, &block)
        return nil
        
      else
        unless recursive or name or namespace
          return @children
          
        else
          result = []
          each_child(recursive, namespace, name) { |child|
            result << child
          }
          return result
        end
      end
    end

Returns a string representation of the children tags.

linePrefix:A prefix to insert before every line.
s:a String that receives the string representation

TODO: break up long lines using the backslash

[Source]

# File lib/sdl4r/tag.rb, line 833
    def children_to_string(line_prefix = "", s = "")
      @children.each do |child|
        s << child.to_string(line_prefix) << $/
      end
      
      return s
    end

Returns the values of all the children with the given name. If the child has more than one value, all the values will be added as an array. If the child has no value, nil will be added. The search is not recursive.

name:if nil, all children are considered (nil by default).

[Source]

# File lib/sdl4r/tag.rb, line 287
    def children_values(name = nil)
      children_values = []
      each_child(false, name) { |child|
        case child.values.size
        when 0
          children_values << nil
        when 1
          children_values << child.value
        else
          children_values << child.values
        end
      }
      return children_values
    end

Clears the attributes of the specified namespace or all the attributes if namespace is nil.

[Source]

# File lib/sdl4r/tag.rb, line 596
    def clear_attributes(namespace = nil)
      if namespace.nil?
        @attributesByNamespace.clear
      else
        @attributesByNamespace.delete(namespace)
      end
    end

Removes all children.

[Source]

# File lib/sdl4r/tag.rb, line 205
    def clear_children
      @children = []
      nil
    end

Removes all values.

[Source]

# File lib/sdl4r/tag.rb, line 437
    def clear_values
      @values = []
      nil
    end

Returns true if this tag (including all of its values, attributes, and children) is equivalent to the given tag.

Returns true if the tags are equivalet

[Source]

# File lib/sdl4r/tag.rb, line 846
    def eql?(o)
      # this is safe because to_string() dumps the full state
      return o.is_a?(Tag) && o.to_string == to_string;
    end

Indicates whether there is at least an attribute in this Tag.

  has_attribute?

Indicates whether there is the specified attribute exists in this Tag.

  has_attribute?(key)
  has_attribute?(namespace, key)

[Source]

# File lib/sdl4r/tag.rb, line 531
    def has_attribute?(namespace = nil, key = nil)
      namespace, key = to_nns namespace, key

      if namespace or key
        attributes = @attributesByNamespace[namespace]
        return attributes.nil? ? false : attributes.has_key?(key)

      else
        attributes { return true }
        return false
      end
    end

Indicates whether the child Tag of given name exists.

name:name of the searched child Tag

[Source]

# File lib/sdl4r/tag.rb, line 330
    def has_child?(name)
      !child(name).nil?
    end

Indicates whether there are children Tag.

[Source]

# File lib/sdl4r/tag.rb, line 336
    def has_children?
      !@children.empty?
    end

Returns true if v is a value of this Tag‘s.

[Source]

# File lib/sdl4r/tag.rb, line 416
    def has_value?(v)
      @values.include?(v)
    end

Returns The hash (based on the output from toString())

[Source]

# File lib/sdl4r/tag.rb, line 854
    def hash
      return to_string.hash
    end

Sets the name of this Tag.

Raises ArgumentError if the name is not a legal SDL

    identifier (see SDL4R#validate_identifier)

[Source]

# File lib/sdl4r/tag.rb, line 669
    def name=(a_name)
      a_name = a_name.to_s
      SDL4R.validate_identifier(a_name)
      @name = a_name
    end

The namespace to set. nil will be coerced to the empty string.

Raises ArgumentError if the namespace is non-blank and is not

    a legal SDL identifier (see {@link SDL4R#validate_identifier(String)})

[Source]

# File lib/sdl4r/tag.rb, line 680
    def namespace=(a_namespace)
      a_namespace = a_namespace.to_s
      SDL4R.validate_identifier(a_namespace) unless a_namespace.empty?
      @namespace = a_namespace
    end

Creates a new child tag. Can take a block so that you can write something like:

  car = Tag.new("car") do
    new_child("wheels") do
      self << 4
    end
  end

The context of execution of the given block is the child instance

Returns the created child Tag.

[Source]

# File lib/sdl4r/tag.rb, line 132
    def new_child(*args, &block)
      return add_child Tag.new(*args, &block)
    end

Adds all the tags specified in the given IO, String, Pathname or URI to this Tag.

Returns this Tag after adding all the children read from input.

[Source]

# File lib/sdl4r/tag.rb, line 690
    def read(input)
      if input.is_a? String
        read_from_io(true) { StringIO.new(input) }

      elsif input.is_a? Pathname
        read_from_io(true) { input.open("r:UTF-8") }

      elsif input.is_a? URI
        read_from_io(true) { input.open }

      else
        read_from_io(false) { input }
      end
      
      return self
    end

Reads and parses the io returned by the specified block and closes this io if close_io is true.

[Source]

# File lib/sdl4r/tag.rb, line 709
    def read_from_io(close_io)
      io = yield

      begin
        Parser.new(io).parse.each do |tag|
          add_child(tag)
        end

      ensure
        if close_io
          io.close rescue IOError
        end
      end
    end
  remove_attribute(key)
  remove_attribute(namespace, key)

Removes the attribute, whose name and namespace are specified.

key:name of the removed atribute
namespace:namespace of the removed attribute (equal to "", default namespace, by default)

Returns the value of the removed attribute or nil if it didn‘t exist.

[Source]

# File lib/sdl4r/tag.rb, line 587
    def remove_attribute(namespace, key = nil)
      namespace, key = to_nns namespace, key
      attributes = @attributesByNamespace[namespace]
      return attributes.nil? ? nil : attributes.delete(key)
    end

Remove a child from this Tag

child:the child to remove

Returns true if the child exists and is removed

[Source]

# File lib/sdl4r/tag.rb, line 199
    def remove_child(child)
      return !@children.delete(child).nil?
    end

Removes the first occurence of the specified value from this Tag.

v:The value to remove

Returns true If the value exists and is removed

[Source]

# File lib/sdl4r/tag.rb, line 426
    def remove_value(v)
      index = @values.index(v)
      if index
        return !@values.delete_at(index).nil?
      else
        return false
      end
    end
  set_attribute(key, value)
  set_attribute(namespace, key, value)

Set an attribute in the given namespace for this tag. The allowable attribute value types are the same as those allowed for {@link addValue(Object)}

namespace:The namespace for this attribute
key:The attribute key
value:The attribute value

@throws IllegalArgumentException if the key is not a legal SDL

    identifier (see {@link SDL4R#validateIdentifier(String)}), or the
    namespace is non-blank and is not a legal SDL identifier, or the
    value is not a legal SDL type

[Source]

# File lib/sdl4r/tag.rb, line 488
    def set_attribute(namespace, key, value = :default)
      if value == :default
        value = key
        key = namespace
        namespace = ""
      end

      raise ArgumentError, "attribute namespace must be a String" unless namespace.is_a? String
      raise ArgumentError, "attribute key must be a String" unless key.is_a? String
      raise ArgumentError, "attribute key cannot be empty" if key.empty?

      SDL4R.validate_identifier(namespace) unless namespace.empty?
      SDL4R.validate_identifier(key)

      attributes = @attributesByNamespace[namespace]
      
      if attributes.nil?
        attributes = {}
        @attributesByNamespace[namespace] = attributes
      end
      
      attributes[key] = SDL4R.coerce_or_fail(value)
    end
  set_attributes(attribute_hash)
  set_attributes(namespace, attribute_hash)

Sets the attributes specified by a Hash in the given namespace in one operation. The previous attributes of the specified namespace are removed. See set_attribute for allowable attribute value types.

attributes:a Hash where keys are attribute keys
namespace:"" (default namespace) by default

Raises an ArgumentError if any key in the map is not a legal SDL

    identifier (see SDL4R#validate_identifier), or any value
    is not a legal SDL type.

[Source]

# File lib/sdl4r/tag.rb, line 637
    def set_attributes(namespace, attribute_hash = nil)
      if attribute_hash.nil?
        attribute_hash = namespace
        namespace = ""
      end
      
      raise ArgumentError, "namespace can't be nil" if namespace.nil?
      raise ArgumentError, "attribute_hash should be a Hash" unless attribute_hash.is_a? Hash

      namespace_attributes = @attributesByNamespace[namespace]
      namespace_attributes.clear if namespace_attributes
      
      attribute_hash.each_pair do |key, value|
          # Calling set_attribute() is required to ensure validations
          set_attribute(namespace, key, value)
      end
    end

Returns a new Hash where the children‘s names as keys and their values as the key‘s value. Example:

  child1 "toto"
  child2 2

would give

  { "child1" => "toto", "child2" => 2 }

[Source]

# File lib/sdl4r/tag.rb, line 375
    def to_child_hash
      hash = {}
      children { |child| hash[child.name] = child.value }
      return hash
    end

Returns a new Hash where the children‘s names as keys and their values as the key‘s value. Values are converted to Strings. nil values become empty Strings. Example:

  child1 "toto"
  child2 2
  child3 null

would give

  { "child1" => "toto", "child2" => "2", "child3" => "" }

[Source]

# File lib/sdl4r/tag.rb, line 393
    def to_child_string_hash
      hash = {}
      children do |child|
        # FIXME: it is quite hard to be sure whether we should mimic the Java version
        # as there might be a lot of values that don't translate nicely to Strings.
        hash[child.name] = child.value.to_s
      end
      return hash
    end

Get a String representation of this SDL Tag. This method returns a complete description of the Tag‘s state using SDL (i.e. the output can be parsed by {@link read(String)})

Returns A string representation of this tag using SDL

[Source]

# File lib/sdl4r/tag.rb, line 766
    def to_s
      to_string
    end
linePrefix:A prefix to insert before every line.

Returns A string representation of this tag using SDL

TODO: break up long lines using the backslash

[Source]

# File lib/sdl4r/tag.rb, line 776
    def to_string(line_prefix = "", indent = "\t")
      line_prefix = "" if line_prefix.nil?
      s = ""
      s << line_prefix
      
      if name == "content" && namespace.empty?
        skip_value_space = true
      else
        skip_value_space = false
        s << "#{namespace}:" unless namespace.empty?
        s << name
      end

      # output values
      values do |value|
        if skip_value_space
          skip_value_space = false
        else
          s << " "
        end
        s << SDL4R.format(value, true, line_prefix, indent)
      end

      # output attributes
      unless @attributesByNamespace.empty?
        all_attributes_hash = attributes
        all_attributes_array = all_attributes_hash.sort { |a, b|
          namespace1, name1 = a[0].split(':')
          namespace1, name1 = "", namespace1 if name1.nil?
          namespace2, name2 = b[0].split(':')
          namespace2, name2 = "", namespace2 if name2.nil?

          diff = namespace1 <=> namespace2
          diff == 0 ? name1 <=> name2 : diff
        }
        all_attributes_array.each do |attribute_name, attribute_value|
          s << " " << attribute_name << '=' << SDL4R.format(attribute_value, true)
        end
      end

      # output children
      unless @children.empty?
        s << " {#{$/}"
        children_to_string(line_prefix + indent, s)
        s << line_prefix << ?}
      end

      return s
    end

Returns a string containing an XML representation of this tag. Values will be represented using _val0, _val1, etc.

line_prefix:A prefix to insert before every line.
uri_by_namespace:a Hash giving the URIs for the namespaces. Nil to ignore this.

[Source]

# File lib/sdl4r/tag.rb, line 864
    def to_xml_string(line_prefix = "", uri_by_namespace = nil)
      line_prefix ||= ""
  
      s = ""
      s << line_prefix << ?<
      s << "#{namespace}:" unless namespace.empty?
      s << name

      # output namespace declarations
      if uri_by_namespace
        uri_by_namespace.each_pair do |namespace, uri|
          if namespace
            s << " xmlns:#{namespace}=\"#{uri}\""
          else
            s << " xmlns=\"#{uri}\""
          end
        end
      end
      
      # output values
      unless @values.empty?
        i = 0
        @values.each do |value|
          s << " _val" << i.to_s << "=\"" << SDL4R.format(value, false) << "\""
          i += 1
        end
      end
      
      # output attributes
      if has_attribute?
        attributes do |attribute_namespace, attribute_name, attribute_value|
          s << " "
          s << "#{attribute_namespace}:" unless attribute_namespace.empty?
          s << attribute_name << "=\"" << SDL4R.format(attribute_value, false) << ?"
        end
      end
  
      if @children.empty?
        s << "/>"
      else
        s << ">\n"
        @children.each do |child|
          s << child.to_xml_string(line_prefix + "    ") << ?\n
        end
        
        s << line_prefix << "</"
        s << "#{namespace}:" unless namespace.empty?
        s << name << ?>
      end
  
      return s
    end

A convenience method that returns the first value.

[Source]

# File lib/sdl4r/tag.rb, line 228
    def value
      @values[0]
    end

A convenience method that sets the first value in the value list. See {@link addValue(Object)} for legal types.

value:The value to be set.

Raises

ArgumentError:if the value is not a legal SDL type

[Source]

# File lib/sdl4r/tag.rb, line 220
    def value=(value)
      @values[0] = SDL4R.coerce_or_fail(value)
      nil
    end

Returns an Array of the values of this Tag or enumerates them.

  tag.values # => [123, "spices"]
  tag.values { |value| puts value }

[Source]

# File lib/sdl4r/tag.rb, line 447
    def values # :yields: value
      if block_given?
        @values.each { |v| yield v }
        nil
      else
        return @values
      end
    end

Set the values for this tag. See {@link addValue(Object)} for legal value types.

values:The new values

@throws IllegalArgumentException if the collection contains any values

    which are not legal SDL types

[Source]

# File lib/sdl4r/tag.rb, line 463
    def values=(someValues)
      @values.clear()
      someValues.to_a.each { |v|
        # this is required to ensure validation of types
        add_value(v)
      }
      nil
    end

Write this tag out to the given IO or StringIO or String (optionally clipping the root.) Returns output.

output:an IO or StringIO or a String to write to
include_root:if true this tag will be written out as the root element, if false only the
  children will be written. False by default.

[Source]

# File lib/sdl4r/tag.rb, line 732
    def write(output, include_root = false)
      if output.is_a? String
        io = StringIO.new(output)
        close_io = true # indicates we close the IO ourselves
      elsif output.is_a? IO or output.is_a? StringIO
        io = output
        close_io = false # let the caller close the IO
      else
        raise ArgumentError, "'output' should be a String or an IO but was #{output.class}"
      end
      
      if include_root
        io << to_s
      else
        first = true
        children do |child|
          io << $/ unless first
          first = false
          io << child.to_s
        end
      end
      
      io.close() if close_io

      output
    end

[Validate]