Class: DICOM::Parent
- Inherits:
-
Object
- Object
- DICOM::Parent
- Includes:
- Logging
- Defined in:
- lib/dicom/parent.rb,
lib/dicom/d_read.rb,
lib/dicom/d_write.rb
Overview
Super class which contains common code for all parent elements.
Inheritance
Since all parents inherit from this class, these methods are available to instances of the following classes:
-
DObject
-
Item
-
Sequence
Instance Method Summary collapse
-
#[](tag_or_index) ⇒ Element, ...
Retrieves the child element matching the specified element tag or item index.
-
#add(element, options = {}) ⇒ Object
Adds an Element or Sequence instance to self (where self can be either a DObject or an Item).
-
#children ⇒ Array<Element, Item, Sequence>
Retrieves all (immediate) child elementals in an array (sorted by element tag).
-
#children? ⇒ Boolean
Checks if an element actually has any child elementals (elements/items/sequences).
-
#count ⇒ Integer
Gives the number of elements connected directly to this parent.
-
#count_all ⇒ Integer
Gives the total number of elements connected to this parent.
-
#delete(tag_or_index, options = {}) ⇒ Object
Deletes the specified element from this parent.
-
#delete_children ⇒ Object
Deletes all child elements from this parent.
-
#delete_group(group_string) ⇒ Object
Deletes all elements of the specified group from this parent.
-
#delete_private ⇒ Object
Deletes all private data/sequence elements from this parent.
-
#delete_retired ⇒ Object
Deletes all retired data/sequence elements from this parent.
-
#each(&block) ⇒ Object
Iterates all children of this parent, calling
block
for each child. -
#each_element(&block) ⇒ Object
Iterates the child elements of this parent, calling
block
for each element. -
#each_item(&block) ⇒ Object
Iterates the child items of this parent, calling
block
for each item. -
#each_sequence(&block) ⇒ Object
Iterates the child sequences of this parent, calling
block
for each sequence. -
#each_tag(&block) ⇒ Object
Iterates the child tags of this parent, calling
block
for each tag. -
#elements ⇒ Array<Element>
Retrieves all child elements of this parent in an array.
-
#elements? ⇒ Boolean
A boolean which indicates whether the parent has any child elements.
-
#encode_children(old_endian) ⇒ Object
Re-encodes the binary data strings of all child Element instances.
-
#exists?(tag_or_index) ⇒ Boolean
Checks whether a specific data element tag is defined for this parent.
-
#group(group_string) ⇒ Array<Element, Item, Sequence>
Returns an array of all child elements that belongs to the specified group.
-
#handle_print(index, max_digits, max_name, max_length, max_generations, visualization, options = {}) ⇒ Array
Gathers the desired information from the selected data elements and processes this information to make a text output which is nicely formatted.
-
#inspect ⇒ String
Gives a string containing a human-readable hash representation of the parent.
-
#is_parent? ⇒ Boolean
Checks if an elemental is a parent.
-
#items ⇒ Array<Item>
Retrieves all child items of this parent in an array.
-
#items? ⇒ Boolean
A boolean which indicates whether the parent has any child items.
-
#length=(new_length) ⇒ Object
Sets the length of a Sequence or Item.
-
#max_lengths ⇒ Object
Finds and returns the maximum character lengths of name and length which occurs for any child element, as well as the maximum number of generations of elements.
-
#method_missing(sym, *args, &block) ⇒ Object
Handles missing methods, which in our case is intended to be dynamic method names matching DICOM elements in the dictionary.
-
#parse(bin, syntax, switched = false, explicit = true) ⇒ Object
Loads data from an encoded DICOM string and creates items and elements which are linked to this instance.
-
#print(options = {}) ⇒ Array<String>
Prints all child elementals of this particular parent.
-
#representation ⇒ String
Gives a string which represents this DICOM parent.
-
#reset_length ⇒ Object
Resets the length of a Sequence or Item to -1, which is the number used for ‘undefined’ length.
-
#respond_to?(method, include_private = false) ⇒ Boolean
Checks if the parent responds to the given method (symbol) (whether the method is defined or not).
-
#sequences ⇒ Array<Sequence>
Retrieves all child sequences of this parent in an array.
-
#sequences? ⇒ Boolean
A boolean which indicates whether the parent has any child sequences.
-
#to_hash ⇒ Hash
Builds a nested hash containing all children of this parent.
-
#to_json ⇒ String
Builds a json string containing a human-readable representation of the parent.
-
#to_yaml ⇒ String
Returns a yaml string containing a human-readable representation of the parent.
-
#value(tag) ⇒ String, ...
Gives the value of a specific Element child of this parent.
Methods included from Logging
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(sym, *args, &block) ⇒ Object
Handles missing methods, which in our case is intended to be dynamic method names matching DICOM elements in the dictionary.
When a dynamic method name is matched against a DICOM element, this method:
-
Returns the element if the method name suggests an element retrieval, and the element exists.
-
Returns nil if the method name suggests an element retrieval, but the element doesn’t exist.
-
Returns a boolean, if the method name suggests a query (?), based on whether the matched element exists or not.
-
When the method name suggests assignment (=), an element is created with the supplied arguments, or if the argument is nil, the element is deleted.
-
When a dynamic method name is not matched against a DICOM element, and the method is not defined by the parent, a NoMethodError is raised.
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 |
# File 'lib/dicom/parent.rb', line 455 def method_missing(sym, *args, &block) s = sym.to_s action = s[-1] # Try to match the method against a tag from the dictionary: tag = LIBRARY.as_tag(s) || LIBRARY.as_tag(s[0..-2]) if tag if action == '?' # Query: return self.exists?(tag) elsif action == '=' # Assignment: unless args.length==0 || args[0].nil? # What kind of element to create? if tag == 'FFFE,E000' return self.add_item elsif LIBRARY.element(tag).vr == 'SQ' return self.add(Sequence.new(tag)) else return self.add(Element.new(tag, *args)) end else return self.delete(tag) end else # Retrieval: return self[tag] end end # Forward to Object#method_missing: super end |
Instance Method Details
#[](tag_or_index) ⇒ Element, ...
Retrieves the child element matching the specified element tag or item index.
Only immediate children are searched. Grandchildren etc. are not included.
27 28 29 30 |
# File 'lib/dicom/parent.rb', line 27 def [](tag_or_index) formatted = tag_or_index.is_a?(String) ? tag_or_index.upcase : tag_or_index return @tags[formatted] end |
#add(element, options = {}) ⇒ Object
Items can not be added with this method (use add_item instead).
Adds an Element or Sequence instance to self (where self can be either a DObject or an Item).
option options [Boolean] :no_follow when true, the method does not update the parent attribute of the child that is added
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/dicom/parent.rb', line 44 def add(element, ={}) unless element.is_a?(Item) unless self.is_a?(Sequence) # Does the element's binary value need to be reencoded? reencode = true if element.is_a?(Element) && element.endian != stream.str_endian # If we are replacing an existing Element, we need to make sure that this Element's parent value is erased before proceeding. self[element.tag].parent = nil if exists?(element.tag) # Add the element, and set its parent attribute: @tags[element.tag] = element element.parent = self unless [:no_follow] # As the element has been moved in place, perform re-encode if indicated: element.value = element.value if reencode else raise "A Sequence is only allowed to have Item elements added to it. Use add_item() instead if the intention is to add an Item." end else raise ArgumentError, "An Item is not allowed as a parameter to the add() method. Use add_item() instead." end end |
#children ⇒ Array<Element, Item, Sequence>
Retrieves all (immediate) child elementals in an array (sorted by element tag).
70 71 72 |
# File 'lib/dicom/parent.rb', line 70 def children return @tags.sort.transpose[1] || Array.new end |
#children? ⇒ Boolean
Checks if an element actually has any child elementals (elements/items/sequences).
Notice the subtle difference between the children? and is_parent? methods. While they will give the same result in most real use cases, they differ when used on parent elements that do not have any children added yet.
For example, when called on an empty Sequence, the children? method will return false, whereas the is_parent? method still returns true.
85 86 87 88 89 90 91 |
# File 'lib/dicom/parent.rb', line 85 def children? if @tags.length > 0 return true else return false end end |
#count ⇒ Integer
Gives the number of elements connected directly to this parent.
This count does NOT include the number of elements contained in any possible child elements.
99 100 101 |
# File 'lib/dicom/parent.rb', line 99 def count return @tags.length end |
#count_all ⇒ Integer
Gives the total number of elements connected to this parent.
This count includes all the elements contained in any possible child elements.
109 110 111 112 113 114 115 116 |
# File 'lib/dicom/parent.rb', line 109 def count_all # Iterate over all elements, and repeat recursively for all elements which themselves contain children. total_count = count @tags.each_value do |value| total_count += value.count_all if value.children? end return total_count end |
#delete(tag_or_index, options = {}) ⇒ Object
Deletes the specified element from this parent.
option options [Boolean] :no_follow when true, the method does not update the parent attribute of the child that is deleted
128 129 130 131 132 133 134 135 136 |
# File 'lib/dicom/parent.rb', line 128 def delete(tag_or_index, ={}) check_key(tag_or_index, :delete) # We need to delete the specified child element's parent reference in addition to removing it from the tag Hash. element = self[tag_or_index] if element element.parent = nil unless [:no_follow] @tags.delete(tag_or_index) end end |
#delete_children ⇒ Object
Deletes all child elements from this parent.
140 141 142 143 144 |
# File 'lib/dicom/parent.rb', line 140 def delete_children @tags.each_key do |tag| delete(tag) end end |
#delete_group(group_string) ⇒ Object
Deletes all elements of the specified group from this parent.
152 153 154 155 156 157 |
# File 'lib/dicom/parent.rb', line 152 def delete_group(group_string) group_elements = group(group_string) group_elements.each do |element| delete(element.tag) end end |
#delete_private ⇒ Object
Deletes all private data/sequence elements from this parent.
166 167 168 169 170 171 172 |
# File 'lib/dicom/parent.rb', line 166 def delete_private # Iterate all children, and repeat recursively if a child itself has children, to delete all private data elements: children.each do |element| delete(element.tag) if element.tag.private? element.delete_private if element.children? end end |
#delete_retired ⇒ Object
Deletes all retired data/sequence elements from this parent.
179 180 181 182 183 184 185 186 |
# File 'lib/dicom/parent.rb', line 179 def delete_retired # Iterate all children, and repeat recursively if a child itself has children, to delete all retired elements: children.each do |element| dict_element = LIBRARY.element(element.tag) delete(element.tag) if dict_element && dict_element.retired? element.delete_retired if element.children? end end |
#each(&block) ⇒ Object
Iterates all children of this parent, calling block
for each child.
190 191 192 |
# File 'lib/dicom/parent.rb', line 190 def each(&block) children.each_with_index(&block) end |
#each_element(&block) ⇒ Object
Iterates the child elements of this parent, calling block
for each element.
196 197 198 |
# File 'lib/dicom/parent.rb', line 196 def each_element(&block) elements.each_with_index(&block) if children? end |
#each_item(&block) ⇒ Object
Iterates the child items of this parent, calling block
for each item.
202 203 204 |
# File 'lib/dicom/parent.rb', line 202 def each_item(&block) items.each_with_index(&block) if children? end |
#each_sequence(&block) ⇒ Object
Iterates the child sequences of this parent, calling block
for each sequence.
208 209 210 |
# File 'lib/dicom/parent.rb', line 208 def each_sequence(&block) sequences.each_with_index(&block) if children? end |
#each_tag(&block) ⇒ Object
Iterates the child tags of this parent, calling block
for each tag.
214 215 216 |
# File 'lib/dicom/parent.rb', line 214 def each_tag(&block) @tags.each_key(&block) end |
#elements ⇒ Array<Element>
Retrieves all child elements of this parent in an array.
222 223 224 |
# File 'lib/dicom/parent.rb', line 222 def elements children.select { |child| child.is_a?(Element)} end |
#elements? ⇒ Boolean
A boolean which indicates whether the parent has any child elements.
230 231 232 |
# File 'lib/dicom/parent.rb', line 230 def elements? elements.any? end |
#encode_children(old_endian) ⇒ Object
This method is only intended for internal library use, but for technical reasons (the fact that is called between instances of different classes), can’t be made private.
Re-encodes the binary data strings of all child Element instances. This also includes all the elements contained in any possible child elements.
241 242 243 244 245 246 247 248 249 250 |
# File 'lib/dicom/parent.rb', line 241 def encode_children(old_endian) # Cycle through all levels of children recursively: children.each do |element| if element.children? element.encode_children(old_endian) elsif element.is_a?(Element) encode_child(element, old_endian) end end end |
#exists?(tag_or_index) ⇒ Boolean
Checks whether a specific data element tag is defined for this parent.
259 260 261 262 263 264 265 |
# File 'lib/dicom/parent.rb', line 259 def exists?(tag_or_index) if self[tag_or_index] return true else return false end end |
#group(group_string) ⇒ Array<Element, Item, Sequence>
Returns an array of all child elements that belongs to the specified group.
272 273 274 275 276 277 278 279 |
# File 'lib/dicom/parent.rb', line 272 def group(group_string) raise ArgumentError, "Expected String, got #{group_string.class}." unless group_string.is_a?(String) found = Array.new children.each do |child| found << child if child.tag.group == group_string.upcase end return found end |
#handle_print(index, max_digits, max_name, max_length, max_generations, visualization, options = {}) ⇒ Array
This method is only intended for internal library use, but for technical reasons (the fact that is called between instances of different classes), can’t be made private. The method is used by the print() method to construct its text output.
Gathers the desired information from the selected data elements and processes this information to make a text output which is nicely formatted.
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 |
# File 'lib/dicom/parent.rb', line 298 def handle_print(index, max_digits, max_name, max_length, max_generations, visualization, ={}) # FIXME: This method is somewhat complex, and some simplification, if possible, wouldn't hurt. elements = Array.new s = " " hook_symbol = "|_" last_item_symbol = " " nonlast_item_symbol = "| " children.each_with_index do |element, i| n_parents = element.parents.length # Formatting: Index i_s = s*(max_digits-(index).to_s.length) # Formatting: Name (and Tag) if element.tag == ITEM_TAG # Add index numbers to the Item names: name = "#{element.name} (\##{i})" else name = element.name end n_s = s*(max_name-name.length) # Formatting: Tag tag = "#{visualization.join}#{element.tag}" t_s = s*((max_generations-1)*2+9-tag.length) # Formatting: Length l_s = s*(max_length-element.length.to_s.length) # Formatting Value: if element.is_a?(Element) value = element.value.to_s else value = "" end if [:value_max] value = "#{value[0..([:value_max]-3)]}.." if value.length > [:value_max] end elements << "#{i_s}#{index} #{tag}#{t_s} #{name}#{n_s} #{element.vr} #{l_s}#{element.length} #{value}" index += 1 # If we have child elements, print those elements recursively: if element.children? if n_parents > 1 child_visualization = Array.new child_visualization.replace(visualization) if element == children.first if children.length == 1 # Last item: child_visualization.insert(n_parents-2, last_item_symbol) else # More items follows: child_visualization.insert(n_parents-2, nonlast_item_symbol) end elsif element == children.last # Last item: child_visualization[n_parents-2] = last_item_symbol child_visualization.insert(-1, hook_symbol) else # Neither first nor last (more items follows): child_visualization.insert(n_parents-2, nonlast_item_symbol) end elsif n_parents == 1 child_visualization = Array.new(1, hook_symbol) else child_visualization = Array.new end new_elements, index = element.handle_print(index, max_digits, max_name, max_length, max_generations, child_visualization, ) elements << new_elements end end return elements.flatten, index end |
#inspect ⇒ String
Gives a string containing a human-readable hash representation of the parent.
370 371 372 |
# File 'lib/dicom/parent.rb', line 370 def inspect to_hash.inspect end |
#is_parent? ⇒ Boolean
Checks if an elemental is a parent.
378 379 380 |
# File 'lib/dicom/parent.rb', line 378 def is_parent? return true end |
#items ⇒ Array<Item>
Retrieves all child items of this parent in an array.
386 387 388 |
# File 'lib/dicom/parent.rb', line 386 def items children.select { |child| child.is_a?(Item)} end |
#items? ⇒ Boolean
A boolean which indicates whether the parent has any child items.
394 395 396 |
# File 'lib/dicom/parent.rb', line 394 def items? items.any? end |
#length=(new_length) ⇒ Object
Currently, ruby-dicom does not use sequence/item lengths when writing DICOM files
Sets the length of a Sequence or Item.
(it sets the length to -1, meaning UNDEFINED). Therefore, in practice, it isn’t necessary to use this method, at least as far as writing (valid) DICOM files is concerned.
406 407 408 409 410 411 412 |
# File 'lib/dicom/parent.rb', line 406 def length=(new_length) unless self.is_a?(DObject) @length = new_length else raise "Length can not be set for a DObject instance." end end |
#max_lengths ⇒ Object
This method is only intended for internal library use, but for technical reasons (the fact that is called between instances of different classes), can’t be made private. The method is used by the print() method to achieve a proper format in its output.
Finds and returns the maximum character lengths of name and length which occurs for any child element, as well as the maximum number of generations of elements.
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 |
# File 'lib/dicom/parent.rb', line 421 def max_lengths max_name = 0 max_length = 0 max_generations = 0 children.each do |element| if element.children? max_nc, max_lc, max_gc = element.max_lengths max_name = max_nc if max_nc > max_name max_length = max_lc if max_lc > max_length max_generations = max_gc if max_gc > max_generations end n_length = element.name.length l_length = element.length.to_s.length generations = element.parents.length max_name = n_length if n_length > max_name max_length = l_length if l_length > max_length max_generations = generations if generations > max_generations end return max_name, max_length, max_generations end |
#parse(bin, syntax, switched = false, explicit = true) ⇒ Object
Loads data from an encoded DICOM string and creates items and elements which are linked to this instance.
12 13 14 15 16 |
# File 'lib/dicom/d_read.rb', line 12 def parse(bin, syntax, switched=false, explicit=true) raise ArgumentError, "Invalid argument 'bin'. Expected String, got #{bin.class}." unless bin.is_a?(String) raise ArgumentError, "Invalid argument 'syntax'. Expected String, got #{syntax.class}." unless syntax.is_a?(String) read(bin, signature=false, :syntax => syntax, :switched => switched, :explicit => explicit) end |
#print(options = {}) ⇒ Array<String>
Prints all child elementals of this particular parent. Information such as tag, parent-child relationship, name, vr, length and value is gathered for each element and processed to produce a nicely formatted output.
option options [Integer] :value_max if a value max length is specified, the element values which exceeds this are trimmed option options [String] :file if a file path is specified, the output is printed to this file instead of being printed to the screen
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 |
# File 'lib/dicom/parent.rb', line 502 def print(={}) # FIXME: Perhaps a :children => false option would be a good idea (to avoid lengthy printouts in cases where this would be desirable)? # FIXME: Speed. The new print algorithm may seem to be slower than the old one (observed on complex, hiearchical DICOM files). Perhaps it can be optimized? elements = Array.new # We first gather some properties that is necessary to produce a nicely formatted printout (max_lengths, count_all), # then the actual information is gathered (handle_print), # and lastly, we pass this information on to the methods which print the output (print_file or print_screen). if count > 0 max_name, max_length, max_generations = max_lengths max_digits = count_all.to_s.length visualization = Array.new elements, index = handle_print(start_index=1, max_digits, max_name, max_length, max_generations, visualization, ) if [:file] print_file(elements, [:file]) else print_screen(elements) end else puts "Notice: Object #{self} is empty (contains no data elements)!" end return elements end |
#representation ⇒ String
Gives a string which represents this DICOM parent. The DOBject is is represented by its class name, whereas elemental parents (Sequence, Item) is represented by their tags.
531 532 533 |
# File 'lib/dicom/parent.rb', line 531 def representation self.is_a?(DObject) ? 'DObject' : self.tag end |
#reset_length ⇒ Object
Resets the length of a Sequence or Item to -1, which is the number used for ‘undefined’ length.
537 538 539 540 541 542 543 544 |
# File 'lib/dicom/parent.rb', line 537 def reset_length unless self.is_a?(DObject) @length = -1 @bin = "" else raise "Length can not be set for a DObject instance." end end |
#respond_to?(method, include_private = false) ⇒ Boolean
Checks if the parent responds to the given method (symbol) (whether the method is defined or not).
552 553 554 555 556 557 558 559 |
# File 'lib/dicom/parent.rb', line 552 def respond_to?(method, include_private=false) # Check the library for a tag corresponding to the given method name symbol: return true unless LIBRARY.as_tag(method.to_s).nil? # In case of a query (xxx?) or assign (xxx=), remove last character and try again: return true unless LIBRARY.as_tag(method.to_s[0..-2]).nil? # Forward to Object#respond_to?: super end |
#sequences ⇒ Array<Sequence>
Retrieves all child sequences of this parent in an array.
565 566 567 |
# File 'lib/dicom/parent.rb', line 565 def sequences children.select { |child| child.is_a?(Sequence) } end |
#sequences? ⇒ Boolean
A boolean which indicates whether the parent has any child sequences.
573 574 575 |
# File 'lib/dicom/parent.rb', line 573 def sequences? sequences.any? end |
#to_hash ⇒ Hash
Builds a nested hash containing all children of this parent.
Keys are determined by the key_representation attribute, and data element values are used as values.
-
For private elements, the tag is used for key instead of the key representation, as private tags lacks names.
-
For child-less parents, the key_representation attribute is used as value.
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 |
# File 'lib/dicom/parent.rb', line 585 def to_hash as_hash = Hash.new unless children? if self.is_a?(DObject) as_hash = {} else as_hash[(self.tag.private?) ? self.tag : self.send(DICOM.key_representation)] = nil end else children.each do |child| if child.tag.private? hash_key = child.tag elsif child.is_a?(Item) hash_key = "Item #{child.index}" else hash_key = child.send(DICOM.key_representation) end if child.is_a?(Element) as_hash[hash_key] = child.to_hash[hash_key] else as_hash[hash_key] = child.to_hash end end end return as_hash end |
#to_json ⇒ String
Builds a json string containing a human-readable representation of the parent.
616 617 618 |
# File 'lib/dicom/parent.rb', line 616 def to_json to_hash.to_json end |
#to_yaml ⇒ String
Returns a yaml string containing a human-readable representation of the parent.
624 625 626 |
# File 'lib/dicom/parent.rb', line 624 def to_yaml to_hash.to_yaml end |
#value(tag) ⇒ String, ...
Gives the value of a specific Element child of this parent.
-
Only Element instances have values. Parent elements like Sequence and Item have no value themselves.
-
If the specified tag is that of a parent element, an exception is raised.
640 641 642 643 644 645 646 647 648 649 650 651 |
# File 'lib/dicom/parent.rb', line 640 def value(tag) check_key(tag, :value) if exists?(tag) if self[tag].is_parent? raise ArgumentError, "Illegal parameter '#{tag}'. Parent elements, like the referenced '#{@tags[tag].class}', have no value. Only Element tags are valid." else return self[tag].value end else return nil end end |