dicom 0.9.5 → 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +13 -5
  2. data/{CHANGELOG.rdoc → CHANGELOG.md} +50 -30
  3. data/{CONTRIBUTING.rdoc → CONTRIBUTING.md} +16 -16
  4. data/Gemfile.lock +47 -0
  5. data/README.md +152 -0
  6. data/dicom.gemspec +11 -10
  7. data/lib/dicom.rb +30 -11
  8. data/lib/dicom/anonymizer.rb +654 -649
  9. data/lib/dicom/audit_trail.rb +0 -2
  10. data/lib/dicom/d_client.rb +1 -1
  11. data/lib/dicom/d_library.rb +45 -15
  12. data/lib/dicom/d_object.rb +18 -18
  13. data/lib/dicom/d_read.rb +28 -4
  14. data/lib/dicom/d_write.rb +49 -26
  15. data/lib/dicom/dictionary/{elements.txt → elements.tsv} +0 -0
  16. data/lib/dicom/dictionary/{uids.txt → uids.tsv} +0 -0
  17. data/lib/dicom/element.rb +6 -7
  18. data/lib/dicom/elemental.rb +1 -0
  19. data/lib/dicom/elemental_parent.rb +64 -0
  20. data/lib/dicom/extensions/array.rb +57 -0
  21. data/lib/dicom/extensions/hash.rb +31 -0
  22. data/lib/dicom/extensions/string.rb +126 -0
  23. data/lib/dicom/{constants.rb → general/constants.rb} +29 -38
  24. data/lib/dicom/{deprecated.rb → general/deprecated.rb} +0 -0
  25. data/lib/dicom/{logging.rb → general/logging.rb} +0 -0
  26. data/lib/dicom/{variables.rb → general/methods.rb} +0 -22
  27. data/lib/dicom/general/variables.rb +29 -0
  28. data/lib/dicom/{version.rb → general/version.rb} +1 -1
  29. data/lib/dicom/image_item.rb +0 -2
  30. data/lib/dicom/image_processor.rb +2 -0
  31. data/lib/dicom/item.rb +1 -13
  32. data/lib/dicom/link.rb +2 -1
  33. data/lib/dicom/parent.rb +34 -86
  34. data/lib/dicom/sequence.rb +1 -13
  35. data/lib/dicom/stream.rb +94 -114
  36. data/rakefile.rb +1 -1
  37. metadata +73 -36
  38. data/README.rdoc +0 -149
  39. data/lib/dicom/ruby_extensions.rb +0 -249
@@ -0,0 +1,31 @@
1
+ # Extensions to the Hash class used by the dicom gem.
2
+ #
3
+ class Hash
4
+
5
+ # Creates a gap in the integer keys at the specified index.
6
+ # This is achieved by incrementing by one all existing index keys that are equal or
7
+ # bigger than the given index.
8
+ #
9
+ # @note It is assumed that this hash features integers as keys and items as values!
10
+ # @param [Integer] index the index at which to clear
11
+ # @return [Hash] the modified self
12
+ #
13
+ def create_key_gap_at(index)
14
+ # Extract existing Hash entries to an array:
15
+ pairs = self.sort
16
+ h = Hash.new
17
+ # Change the key of those equal or larger than index and put these key,value pairs back in a new Hash:
18
+ pairs.each do |pair|
19
+ if pair[0] < index
20
+ # The key keeps its old index:
21
+ h[pair[0]] = pair[1]
22
+ else
23
+ # The key (and the value Item) gets its index incremented by one:
24
+ h[pair[0]+1] = pair[1]
25
+ pair[1].index = pair[0]+1
26
+ end
27
+ end
28
+ h
29
+ end
30
+
31
+ end
@@ -0,0 +1,126 @@
1
+ # encoding: UTF-8
2
+
3
+ # Extension to the String class. These mainly facilitate the processing and analysis of element tags.
4
+ # A tag string (as used by the ruby-dicom library) is 9 characters long and of the form 'GGGG,EEEE'
5
+ # (where G represents a group hexadecimal, and E represents an element hexadecimal).
6
+ #
7
+ class String
8
+
9
+ # Checks if a string value LOOKS like a DICOM name - it may still not be valid one.
10
+ #
11
+ # @return [Boolean] true if a string looks like a DICOM name, and false if not
12
+ #
13
+ def dicom_name?
14
+ self == self.dicom_titleize
15
+ end
16
+
17
+ # Checks if a string value LOOKS like a DICOM method name - it may still not be valid one.
18
+ #
19
+ # @return [Boolean] true if a string looks like a DICOM method name, and false if not
20
+ #
21
+ def dicom_method?
22
+ self == self.dicom_underscore
23
+ end
24
+
25
+ # Capitalizes all the words in the string and replaces some characters to make a nicer looking title.
26
+ #
27
+ # @return [String] a formatted, capitalized string
28
+ #
29
+ def dicom_titleize
30
+ self.dicom_underscore.gsub(/_/, ' ').gsub(/\b('?[a-z])/) { $1.capitalize }
31
+ end
32
+
33
+ # Makes an underscored, lowercased version of the string.
34
+ #
35
+ # @return [String] an underscored, lower case string
36
+ #
37
+ def dicom_underscore
38
+ word = self.dup
39
+ word.tr!('-', '_')
40
+ word.downcase!
41
+ word
42
+ end
43
+
44
+ # Divides a string into a number of sub-strings of exactly equal length.
45
+ #
46
+ # @note The length of self must be a multiple of parts, or an exception will be raised.
47
+ # @param [Integer] parts the number of sub-strings to create
48
+ # @return [Array<String>] the divided sub-strings
49
+ #
50
+ def divide(parts)
51
+ parts = parts.to_i
52
+ raise ArgumentError, "Argument must be in the range <1 - self.length (#{self.length})>. Got #{parts}." if parts < 1 or parts > self.length
53
+ raise ArgumentError, "Length of self (#{self.length}) must be a multiple of parts (#{parts})." if (self.length/parts.to_f % 1) != 0
54
+ sub_strings = Array.new
55
+ sub_length = self.length/parts
56
+ start_index = 0
57
+ parts.times {
58
+ sub_strings << self.slice(start_index, sub_length)
59
+ start_index += sub_length
60
+ }
61
+ sub_strings
62
+ end
63
+
64
+ # Extracts the element part of the tag string: The last 4 characters.
65
+ #
66
+ # @return [String] the element part of the tag
67
+ #
68
+ def element
69
+ return self[5..8]
70
+ end
71
+
72
+ # Returns the group part of the tag string: The first 4 characters.
73
+ #
74
+ # @return [String] the group part of the tag
75
+ #
76
+ def group
77
+ return self[0..3]
78
+ end
79
+
80
+ # Returns the "Group Length" ('GGGG,0000') tag which corresponds to the original tag/group string.
81
+ # The original string may either be a 4 character group string, or a 9 character custom tag.
82
+ #
83
+ # @return [String] a group length tag
84
+ #
85
+ def group_length
86
+ if self.length == 4
87
+ return self + ',0000'
88
+ else
89
+ return self.group + ',0000'
90
+ end
91
+ end
92
+
93
+ # Checks if the string is a "Group Length" tag (its element part is '0000').
94
+ #
95
+ # @return [Boolean] true if it is a group length tag, and false if not
96
+ #
97
+ def group_length?
98
+ return (self.element == '0000' ? true : false)
99
+ end
100
+
101
+ # Checks if the string is a private tag (has an odd group number).
102
+ #
103
+ # @return [Boolean] true if it is a private tag, and false if not
104
+ #
105
+ def private?
106
+ return ((self.upcase =~ /\A\h{3}[1,3,5,7,9,B,D,F],\h{4}\z/) == nil ? false : true)
107
+ end
108
+
109
+ # Checks if the string is a valid tag (as defined by ruby-dicom: 'GGGG,EEEE').
110
+ #
111
+ # @return [Boolean] true if it is a valid tag, and false if not
112
+ #
113
+ def tag?
114
+ # Test that the string is composed of exactly 4 HEX characters, followed by a comma, then 4 more HEX characters:
115
+ return ((self.upcase =~ /\A\h{4},\h{4}\z/) == nil ? false : true)
116
+ end
117
+
118
+ # Converts the string to a proper DICOM element method name symbol.
119
+ #
120
+ # @return [Symbol] a DICOM element method name
121
+ #
122
+ def to_element_method
123
+ self.gsub(/^3/,'three_').gsub(/[#*?!]/,' ').gsub(', ',' ').gsub('&','and').gsub(' - ','_').gsub(' / ','_').gsub(/[\s\-\.\,\/\\]/,'_').gsub(/[\(\)\']/,'').gsub(/\_+/, '_').downcase.to_sym
124
+ end
125
+
126
+ end
@@ -1,8 +1,5 @@
1
1
  module DICOM
2
2
 
3
- # Defines the gem root directory in the file system.
4
- ROOT_DIR = "#{File.dirname(__FILE__)}"
5
-
6
3
  # Ruby DICOM's registered DICOM UID root (Implementation Class UID).
7
4
  UID_ROOT = "1.2.826.0.1.3680043.8.641"
8
5
  # Ruby DICOM name & version (max 16 characters).
@@ -109,14 +106,6 @@ module DICOM
109
106
  # System (CPU) Endianness.
110
107
  CPU_ENDIAN = endian_type[Array(x).pack("L*")]
111
108
 
112
- # Custom string used for (un)packing big endian signed short.
113
- CUSTOM_SS = "k*"
114
- # Custom string used for (un)packing big endian signed long.
115
- CUSTOM_SL = "r*"
116
-
117
- # ruby-dicom's library instance (data dictionary).
118
- LIBRARY = DICOM::DLibrary.new
119
-
120
109
  # Transfer Syntaxes (taken from the DICOM Specification PS 3.5, Chapter 10).
121
110
 
122
111
  # General
@@ -184,36 +173,38 @@ module DICOM
184
173
  'GB18030' => 'GB18030',
185
174
  'ISO_IR 192' => 'UTF-8'
186
175
  }
176
+ ENCODING_NAME.default = 'ASCII-8BIT'
187
177
 
188
178
  # The type conversion (method) used for the various value representations.
189
179
  VALUE_CONVERSION = {
190
- 'BY' => :to_i,
191
- 'US' => :to_i,
192
- 'SS' => :to_i,
193
- 'UL' => :to_i,
194
- 'SL' => :to_i,
195
- 'OB' => :to_i,
196
- 'OW' => :to_i,
197
- 'OF' => :to_f,
198
- 'FL' => :to_f,
199
- 'FD' => :to_f,
200
- 'AT' => :to_s,
201
- 'AE' => :to_s,
202
- 'AS' => :to_s,
203
- 'CS' => :to_s,
204
- 'DA' => :to_s,
205
- 'DS' => :to_s,
206
- 'DT' => :to_s,
207
- 'IS' => :to_s,
208
- 'LO' => :to_s,
209
- 'LT' => :to_s,
210
- 'PN' => :to_s,
211
- 'SH' => :to_s,
212
- 'ST' => :to_s,
213
- 'TM' => :to_s,
214
- 'UI' => :to_s,
215
- 'UT' => :to_s
216
- }
180
+ 'BY' => :to_i,
181
+ 'US' => :to_i,
182
+ 'SS' => :to_i,
183
+ 'UL' => :to_i,
184
+ 'SL' => :to_i,
185
+ 'OB' => :to_i,
186
+ 'OW' => :to_i,
187
+ 'OF' => :to_f,
188
+ 'FL' => :to_f,
189
+ 'FD' => :to_f,
190
+ 'AT' => :to_s,
191
+ 'AE' => :to_s,
192
+ 'AS' => :to_s,
193
+ 'CS' => :to_s,
194
+ 'DA' => :to_s,
195
+ 'DS' => :to_s,
196
+ 'DT' => :to_s,
197
+ 'IS' => :to_s,
198
+ 'LO' => :to_s,
199
+ 'LT' => :to_s,
200
+ 'PN' => :to_s,
201
+ 'SH' => :to_s,
202
+ 'ST' => :to_s,
203
+ 'TM' => :to_s,
204
+ 'UI' => :to_s,
205
+ 'UT' => :to_s
206
+ }
207
+ VALUE_CONVERSION.default = :to_s
217
208
 
218
209
  end
219
210
 
@@ -2,17 +2,6 @@ module DICOM
2
2
 
3
3
  class << self
4
4
 
5
- #--
6
- # Module attributes:
7
- #++
8
-
9
- # The ruby-dicom image processor to be used.
10
- attr_accessor :image_processor
11
- # The key representation for hashes, json, yaml.
12
- attr_accessor :key_representation
13
- # Source Application Entity Title (gets written to the DICOM header in files where it is undefined).
14
- attr_accessor :source_app_title
15
-
16
5
  #--
17
6
  # Module methods:
18
7
  #++
@@ -91,15 +80,4 @@ module DICOM
91
80
 
92
81
  end
93
82
 
94
- #--
95
- # Default variable settings:
96
- #++
97
-
98
- # The default image processor.
99
- self.image_processor = :rmagick
100
- # The default key representation.
101
- self.key_representation = :name
102
- # The default source application entity title.
103
- self.source_app_title = 'RUBY-DICOM'
104
-
105
83
  end
@@ -0,0 +1,29 @@
1
+ module DICOM
2
+
3
+ class << self
4
+
5
+ #--
6
+ # Module attributes:
7
+ #++
8
+
9
+ # The ruby-dicom image processor to be used.
10
+ attr_accessor :image_processor
11
+ # The key representation for hashes, json, yaml.
12
+ attr_accessor :key_representation
13
+ # Source Application Entity Title (gets written to the DICOM header in files where it is undefined).
14
+ attr_accessor :source_app_title
15
+
16
+ end
17
+
18
+ #--
19
+ # Default variable settings:
20
+ #++
21
+
22
+ # The default image processor.
23
+ self.image_processor = :rmagick
24
+ # The default key representation.
25
+ self.key_representation = :name
26
+ # The default source application entity title.
27
+ self.source_app_title = 'RUBY-DICOM'
28
+
29
+ end
@@ -1,6 +1,6 @@
1
1
  module DICOM
2
2
 
3
3
  # The ruby-dicom version string.
4
- VERSION = '0.9.5'
4
+ VERSION = '0.9.6'
5
5
 
6
6
  end
@@ -119,7 +119,6 @@ module DICOM
119
119
  # image frames, the first image frame is returned, unless the :frame option is used.
120
120
  #
121
121
  # @note Creates an image object in accordance with the selected image processor. Available processors are :rmagick and :mini_magick.
122
- # @note When calling this method the corresponding image processor gem must have been loaded in advance (example: require 'RMagick').
123
122
  #
124
123
  # @param [Hash] options the options to use for extracting the image
125
124
  # @option options [Integer] :frame for DICOM objects containing multiple frames, this option can be used to extract a specific image frame (defaults to 0)
@@ -145,7 +144,6 @@ module DICOM
145
144
  # the image related elements in the DICOM object.
146
145
  #
147
146
  # @note Creates an array of image objects in accordance with the selected image processor. Available processors are :rmagick and :mini_magick.
148
- # @note When calling this method the corresponding image processor gem must have been loaded in advance (example: require 'RMagick').
149
147
  #
150
148
  # @param [Hash] options the options to use for extracting the images
151
149
  # @option options [Integer] :frame makes the method return an array containing only the image object corresponding to the specified frame number
@@ -66,8 +66,10 @@ module DICOM
66
66
  def image_module
67
67
  case DICOM.image_processor
68
68
  when :mini_magick
69
+ require 'mini_magick'
69
70
  DcmMiniMagick
70
71
  when :rmagick
72
+ require 'rmagick'
71
73
  DcmRMagick
72
74
  else
73
75
  raise "Uknown image processor #{DICOM.image_processor}"
@@ -9,8 +9,8 @@ module DICOM
9
9
  #
10
10
  class Item < ImageItem
11
11
 
12
- # Include the Elemental mix-in module:
13
12
  include Elemental
13
+ include ElementalParent
14
14
 
15
15
  # The index of this Item in the group of items belonging to its parent. If the Item is without parent, index is nil.
16
16
  attr_accessor :index
@@ -98,18 +98,6 @@ module DICOM
98
98
  state.hash
99
99
  end
100
100
 
101
- # Loads data from an encoded DICOM string and creates
102
- # sequences and elements which are linked to this instance.
103
- #
104
- # @param [String] bin an encoded binary string containing DICOM information
105
- # @param [String] syntax the transfer syntax to use when decoding the DICOM string
106
- #
107
- def parse(bin, syntax)
108
- raise ArgumentError, "Invalid argument 'bin'. Expected String, got #{bin.class}." unless bin.is_a?(String)
109
- raise ArgumentError, "Invalid argument 'syntax'. Expected String, got #{syntax.class}." unless syntax.is_a?(String)
110
- read(bin, signature=false, :syntax => syntax)
111
- end
112
-
113
101
  # Returns self.
114
102
  #
115
103
  # @return [Item] self
@@ -1426,6 +1426,7 @@ module DICOM
1426
1426
  response = IO.select([@session], nil, nil, @timeout)
1427
1427
  if response.nil?
1428
1428
  logger.error("No answer was received within the specified timeout period. Aborting.")
1429
+ stop_receiving
1429
1430
  else
1430
1431
  data = @session.recv(@max_receive_size)
1431
1432
  end
@@ -1486,7 +1487,7 @@ module DICOM
1486
1487
  if info[:role_negotiation]
1487
1488
  pos = 3
1488
1489
  info[:role_negotiation].each do |role|
1489
- msg = Stream.new(message, @net_endian)
1490
+ msg = Stream.new('', @net_endian)
1490
1491
  uid = role[:sop_uid]
1491
1492
  # Length of UID (2 bytes):
1492
1493
  msg.encode_first(uid.length, "US")
@@ -61,76 +61,6 @@ module DICOM
61
61
  end
62
62
  end
63
63
 
64
- # Adds a child item to a Sequence (or Item in some cases where pixel data is encapsulated).
65
- #
66
- # If no existing Item is given, a new item will be created and added.
67
- #
68
- # @note Items are specified by index (starting at 0) instead of a tag string!
69
- #
70
- # @param [Item] item the Item instance to be added
71
- # @param [Hash] options the options used for adding the item
72
- # option options [Integer] :if specified, forces the item to be inserted at that specific index (Item number)
73
- # option options [Boolean] :no_follow when true, the method does not update the parent attribute of the child that is added
74
- # * <tt>options</tt> -- A hash of parameters.
75
- # @example Add an empty Item to a specific Sequence
76
- # dcm["3006,0020"].add_item
77
- # @example Add an existing Item at the 2nd item position/index in the specific Sequence
78
- # dcm["3006,0020"].add_item(my_item, :index => 1)
79
- #
80
- def add_item(item=nil, options={})
81
- unless self.is_a?(DObject)
82
- if item
83
- if item.is_a?(Item)
84
- if options[:index]
85
- # This Item will take a specific index, and all existing Items with index higher or equal to this number will have their index increased by one.
86
- # Check if index is valid (must be an existing index):
87
- if options[:index] >= 0
88
- # If the index value is larger than the max index present, we dont need to modify the existing items.
89
- if options[:index] < @tags.length
90
- # Extract existing Hash entries to an array:
91
- pairs = @tags.sort
92
- @tags = Hash.new
93
- # Change the key of those equal or larger than index and put these key,value pairs back in a new Hash:
94
- pairs.each do |pair|
95
- if pair[0] < options[:index]
96
- @tags[pair[0]] = pair[1] # (Item keeps its old index)
97
- else
98
- @tags[pair[0]+1] = pair[1]
99
- pair[1].index = pair[0]+1 # (Item gets updated with its new index)
100
- end
101
- end
102
- else
103
- # Set the index value one higher than the already existing max value:
104
- options[:index] = @tags.length
105
- end
106
- #,Add the new Item and set its index:
107
- @tags[options[:index]] = item
108
- item.index = options[:index]
109
- else
110
- raise ArgumentError, "The specified index (#{options[:index]}) is out of range (Must be a positive integer)."
111
- end
112
- else
113
- # Add the existing Item to this Sequence:
114
- index = @tags.length
115
- @tags[index] = item
116
- # Let the Item know what index key it's got in it's parent's Hash:
117
- item.index = index
118
- end
119
- # Set ourself as this item's new parent:
120
- item.set_parent(self) unless options[:no_follow]
121
- else
122
- raise ArgumentError, "The specified parameter is not an Item. Only Items are allowed to be added to a Sequence."
123
- end
124
- else
125
- # Create an empty Item with self as parent.
126
- index = @tags.length
127
- item = Item.new(:parent => self)
128
- end
129
- else
130
- raise "An Item #{item} was attempted added to a DObject instance #{self}, which is not allowed."
131
- end
132
- end
133
-
134
64
  # Retrieves all (immediate) child elementals in an array (sorted by element tag).
135
65
  #
136
66
  # @return [Array<Element, Item, Sequence>] the parent's child elementals (an empty array if childless)
@@ -196,12 +126,7 @@ module DICOM
196
126
  # dcm["3006,0020"].delete(1)
197
127
  #
198
128
  def delete(tag_or_index, options={})
199
- if tag_or_index.is_a?(String) or tag_or_index.is_a?(Integer)
200
- raise ArgumentError, "Argument (#{tag_or_index}) is not a valid tag string." if tag_or_index.is_a?(String) && !tag_or_index.tag?
201
- raise ArgumentError, "Negative Integer argument (#{tag_or_index}) is not allowed." if tag_or_index.is_a?(Integer) && tag_or_index < 0
202
- else
203
- raise ArgumentError, "Expected String or Integer, got #{tag_or_index.class}."
204
- end
129
+ check_key(tag_or_index, :delete)
205
130
  # We need to delete the specified child element's parent reference in addition to removing it from the tag Hash.
206
131
  element = self[tag_or_index]
207
132
  if element
@@ -528,13 +453,15 @@ module DICOM
528
453
  # @param [Symbol] sym a method name
529
454
  #
530
455
  def method_missing(sym, *args, &block)
456
+ s = sym.to_s
457
+ action = s[-1]
531
458
  # Try to match the method against a tag from the dictionary:
532
- tag = LIBRARY.as_tag(sym.to_s) || LIBRARY.as_tag(sym.to_s[0..-2])
459
+ tag = LIBRARY.as_tag(s) || LIBRARY.as_tag(s[0..-2])
533
460
  if tag
534
- if sym.to_s[-1..-1] == '?'
461
+ if action == '?'
535
462
  # Query:
536
463
  return self.exists?(tag)
537
- elsif sym.to_s[-1..-1] == '='
464
+ elsif action == '='
538
465
  # Assignment:
539
466
  unless args.length==0 || args[0].nil?
540
467
  # What kind of element to create?
@@ -550,7 +477,7 @@ module DICOM
550
477
  end
551
478
  else
552
479
  # Retrieval:
553
- return self[tag] rescue nil
480
+ return self[tag]
554
481
  end
555
482
  end
556
483
  # Forward to Object#method_missing:
@@ -595,6 +522,16 @@ module DICOM
595
522
  return elements
596
523
  end
597
524
 
525
+ # Gives a string which represents this DICOM parent. The DOBject is
526
+ # is represented by its class name, whereas elemental parents (Sequence,
527
+ # Item) is represented by their tags.
528
+ #
529
+ # @return [String] a representation of the DICOM parent
530
+ #
531
+ def representation
532
+ self.is_a?(DObject) ? 'DObject' : self.tag
533
+ end
534
+
598
535
  # Resets the length of a Sequence or Item to -1, which is the number used for 'undefined' length.
599
536
  #
600
537
  def reset_length
@@ -701,12 +638,7 @@ module DICOM
701
638
  # uid = dcm["3006,0010"][0].value("0020,0052")
702
639
  #
703
640
  def value(tag)
704
- if tag.is_a?(String) or tag.is_a?(Integer)
705
- raise ArgumentError, "Argument (#{tag}) is not a valid tag string." if tag.is_a?(String) && !tag.tag?
706
- raise ArgumentError, "Negative Integer argument (#{tag}) is not allowed." if tag.is_a?(Integer) && tag < 0
707
- else
708
- raise ArgumentError, "Expected String or Integer, got #{tag.class}."
709
- end
641
+ check_key(tag, :value)
710
642
  if exists?(tag)
711
643
  if self[tag].is_parent?
712
644
  raise ArgumentError, "Illegal parameter '#{tag}'. Parent elements, like the referenced '#{@tags[tag].class}', have no value. Only Element tags are valid."
@@ -722,6 +654,22 @@ module DICOM
722
654
  private
723
655
 
724
656
 
657
+ # Checks the given key argument and logs a warning if an obviously
658
+ # incorrect key argument is used.
659
+ #
660
+ # @param [String, Integer] tag_or_index the tag string or item index indentifying a given elemental
661
+ # @param [Symbol] method a representation of the method calling this method
662
+ #
663
+ def check_key(tag_or_index, method)
664
+ if tag_or_index.is_a?(String)
665
+ logger.warn("Parent##{method} called with an invalid tag argument: #{tag_or_index}") unless tag_or_index.tag?
666
+ elsif tag_or_index.is_a?(Integer)
667
+ logger.warn("Parent##{method} called with a negative Integer argument: #{tag_or_index}") if tag_or_index < 0
668
+ else
669
+ logger.warn("Parent##{method} called with an unexpected argument. Expected String or Integer, got: #{tag_or_index.class}")
670
+ end
671
+ end
672
+
725
673
  # Re-encodes the value of a child Element (but only if the
726
674
  # Element encoding is influenced by a shift in endianness).
727
675
  #