Class: SDL4R::Tag

Inherits:
Object
  • Object
show all
Defined in:
lib/sdl4r/tag.rb

Overview

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

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(namespace, name = nil, &block) ⇒ Tag

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.

Raises:

  • (ArgumentError)


95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# 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

Instance Attribute Details

#nameObject

the name of this Tag



47
48
49
# File 'lib/sdl4r/tag.rb', line 47

def name
  @name
end

#namespaceObject

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



52
53
54
# File 'lib/sdl4r/tag.rb', line 52

def namespace
  @namespace
end

Instance Method Details

#<<(o) ⇒ Object

Adds the given object as a child if it is a Tag, as an attribute if it is a Hash => 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.



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# 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

#add_child(child) ⇒ Object

Add a child to this Tag.

child

The child to add

Returns the added child.



142
143
144
145
# File 'lib/sdl4r/tag.rb', line 142

def add_child(child)
  @children.push(child)
  return child
end

#add_value(v) ⇒ Object

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



409
410
411
412
# File 'lib/sdl4r/tag.rb', line 409

def add_value(v)
  @values.push(SDL4R::coerce_or_fail(v))
  return nil
end

#attribute(namespace, key = nil) ⇒ Object

attribute(key)

attribute(namespace, key)

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



518
519
520
521
522
# 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

#attributes(namespace = nil, &block) ⇒ Object

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.



556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
# 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

#attributes=(attribute_hash) ⇒ Object

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

See #set_attributes



660
661
662
# File 'lib/sdl4r/tag.rb', line 660

def attributes=(attribute_hash)
  set_attributes(attribute_hash)
end

#child(recursive = false, name = nil) ⇒ Object

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



313
314
315
316
317
318
319
320
321
322
323
324
# 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

#child_countObject

Returns the number of children Tag.



234
235
236
# File 'lib/sdl4r/tag.rb', line 234

def child_count
  @children.size
end

#children(recursive = false, namespace = nil, name = :DEFAULT, &block) ⇒ Object

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"


258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
# 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

#children_to_string(line_prefix = "", s = "") ⇒ Object

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



833
834
835
836
837
838
839
# 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

#children_values(name = nil) ⇒ Object

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).



287
288
289
290
291
292
293
294
295
296
297
298
299
300
# 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

#clear_attributes(namespace = nil) ⇒ Object

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



596
597
598
599
600
601
602
# File 'lib/sdl4r/tag.rb', line 596

def clear_attributes(namespace = nil)
  if namespace.nil?
    @attributesByNamespace.clear
  else
    @attributesByNamespace.delete(namespace)
  end
end

#clear_childrenObject

Removes all children.



205
206
207
208
# File 'lib/sdl4r/tag.rb', line 205

def clear_children
  @children = []
  nil
end

#clear_valuesObject

Removes all values.



437
438
439
440
# File 'lib/sdl4r/tag.rb', line 437

def clear_values
  @values = []
  nil
end

#eql?(o) ⇒ Boolean Also known as: ==

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

Returns:

  • (Boolean)


846
847
848
849
# 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

#has_attribute?(namespace = nil, key = nil) ⇒ Boolean

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)

Returns:

  • (Boolean)


531
532
533
534
535
536
537
538
539
540
541
542
# 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

#has_child?(name) ⇒ Boolean

Indicates whether the child Tag of given name exists.

name

name of the searched child Tag

Returns:

  • (Boolean)


330
331
332
# File 'lib/sdl4r/tag.rb', line 330

def has_child?(name)
  !child(name).nil?
end

#has_children?Boolean

Indicates whether there are children Tag.

Returns:

  • (Boolean)


336
337
338
# File 'lib/sdl4r/tag.rb', line 336

def has_children?
  !@children.empty?
end

#has_value?(v) ⇒ Boolean

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

Returns:

  • (Boolean)


416
417
418
# File 'lib/sdl4r/tag.rb', line 416

def has_value?(v)
  @values.include?(v)
end

#hashObject

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



854
855
856
# File 'lib/sdl4r/tag.rb', line 854

def hash
  return to_string.hash
end

#new_child(*args, &block) ⇒ Object

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.



132
133
134
# File 'lib/sdl4r/tag.rb', line 132

def new_child(*args, &block)
  return add_child Tag.new(*args, &block)
end

#read(input) ⇒ Object

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.



690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
# 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

#read_from_io(close_io) ⇒ Object

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



709
710
711
712
713
714
715
716
717
718
719
720
721
722
# 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(namespace, key = nil) ⇒ Object

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.



587
588
589
590
591
# 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_child(child) ⇒ Object

Remove a child from this Tag

child

the child to remove

Returns true if the child exists and is removed



199
200
201
# File 'lib/sdl4r/tag.rb', line 199

def remove_child(child)
  return !@children.delete(child).nil?
end

#remove_value(v) ⇒ Object

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



426
427
428
429
430
431
432
433
# 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(namespace, key, value = :default) ⇒ Object

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 #addValue(Object)

namespace

The namespace for this attribute

key

The attribute key

value

The attribute value

Raises:

  • (ArgumentError)


488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
# 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(namespace, attribute_hash = nil) ⇒ Object

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.

Raises:

  • (ArgumentError)


637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
# 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

#to_child_hashObject

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 }


375
376
377
378
379
# File 'lib/sdl4r/tag.rb', line 375

def to_child_hash
  hash = {}
  children { |child| hash[child.name] = child.value }
  return hash
end

#to_child_string_hashObject

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" => "" }


393
394
395
396
397
398
399
400
401
# 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

#to_sObject

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 #read(String))

Returns A string representation of this tag using SDL



766
767
768
# File 'lib/sdl4r/tag.rb', line 766

def to_s
  to_string
end

#to_string(line_prefix = "", indent = "\t") ⇒ Object

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



776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
# 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

#to_xml_string(line_prefix = "", uri_by_namespace = nil) ⇒ Object

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.



864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
# 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

#valueObject

A convenience method that returns the first value.



228
229
230
# File 'lib/sdl4r/tag.rb', line 228

def value
  @values[0]
end

#value=(value) ⇒ Object

A convenience method that sets the first value in the value list. See #addValue(Object) for legal types.

value

The value to be set.

Raises

ArgumentError

if the value is not a legal SDL type



220
221
222
223
# File 'lib/sdl4r/tag.rb', line 220

def value=(value)
  @values[0] = SDL4R.coerce_or_fail(value)
  nil
end

#valuesObject

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

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


447
448
449
450
451
452
453
454
# 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

#values=(someValues) ⇒ Object

Set the values for this tag. See #addValue(Object) for legal value types.

values

The new values



463
464
465
466
467
468
469
470
# 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(output, include_root = false) ⇒ Object

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.


732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
# 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