--- lib/rexml/element.rb (revision 1275) +++ lib/rexml/element.rb (revision 1276) @@ -295,14 +295,9 @@ def add_element element, attrs=nil raise "First argument must be either an element name, or an Element object" if element.nil? el = @elements.add(element) - if attrs.kind_of? Hash - attrs.each do |key, value| - el.attributes[key]=value if key =~ /^xmlns:/ - end - attrs.each do |key, value| - el.attributes[key]=value if key !~ /^xmlns:/ - end - end + attrs.each do |key, value| + el.attributes[key]=Attribute.new(key,value,self) + end if attrs.kind_of? Hash el end @@ -577,7 +572,8 @@ # value:: # Required if +key+ is a String, and ignored if the first argument is # an Attribute. This is a String, and is used as the value - # of the new Attribute. + # of the new Attribute. This should be the unnormalized value of the + # attribute (without entities). # Returns:: the Attribute added # e = Element.new 'e' # e.add_attribute( 'a', 'b' ) #-> <e a='b'/> @@ -1006,10 +1002,11 @@ # name:: an XPath attribute name. Namespaces are relevant here. # Returns:: # the String value of the matching attribute, or +nil+ if no - # matching attribute was found. + # matching attribute was found. This is the unnormalized value + # (with entities expanded). # - # doc = Document.new "<a foo:att='1' bar:att='2' att='3'/>" - # doc.root.attributes['att'] #-> '3' + # doc = Document.new "<a foo:att='1' bar:att='2' att='<'/>" + # doc.root.attributes['att'] #-> '<' # doc.root.attributes['bar:att'] #-> '2' def [](name) attr = get_attribute(name) @@ -1119,7 +1116,15 @@ delete attr return end - value = Attribute.new(name, value) unless value.kind_of? Attribute + element_document = @element.document + unless value.kind_of? Attribute + if @element.document and @element.document.doctype + value = Text::normalize( value, @element.document.doctype ) + else + value = Text::normalize( value, nil ) + end + value = Attribute.new(name, value) + end value.element = @element old_attr = fetch(value.name, nil) if old_attr.nil? ================================================================== --- lib/rexml/attribute.rb (revision 1275) +++ lib/rexml/attribute.rb (revision 1276) @@ -18,16 +18,32 @@ PATTERN = /\s*(#{NAME_STR})\s*=\s*(["'])(.*?)\2/um # Constructor. + # FIXME: The parser doesn't catch illegal characters in attributes + # + # first:: + # Either: an Attribute, which this new attribute will become a + # clone of; or a String, which is the name of this attribute + # second:: + # If +first+ is an Attribute, then this may be an Element, or nil. + # If nil, then the Element parent of this attribute is the parent + # of the +first+ Attribute. If the first argument is a String, + # then this must also be a String, and is the content of the attribute. + # If this is the content, it must be fully normalized (contain no + # illegal characters). + # parent:: + # Ignored unless +first+ is a String; otherwise, may be the Element + # parent of this attribute, or nil. + # # # Attribute.new( attribute_to_clone ) - # Attribute.new( source ) + # Attribute.new( attribute_to_clone, parent_element ) # Attribute.new( "attr", "attr_value" ) # Attribute.new( "attr", "attr_value", parent_element ) def initialize( first, second=nil, parent=nil ) @normalized = @unnormalized = @element = nil if first.kind_of? Attribute self.name = first.expanded_name - @value = first.value + @unnormalized = first.value if second.kind_of? Element @element = second else @@ -36,7 +52,7 @@ elsif first.kind_of? String @element = parent if parent.kind_of? Element self.name = first - @value = second.to_s + @normalized = second.to_s else raise "illegal argument #{first.class.name} to Attribute constructor" end @@ -72,7 +88,7 @@ # Returns true if other is an Attribute and has the same name and value, # false otherwise. def ==( other ) - other.kind_of?(Attribute) and other.name==name and other.value==@value + other.kind_of?(Attribute) and other.name==name and other.value==value end # Creates (and returns) a hash from both the name and value @@ -104,8 +120,9 @@ doctype = doc.doctype if doc end + @normalized = Text::normalize( @unnormalized, doctype ) @unnormalized = nil - @normalized = Text::normalize( @value, doctype ) + @normalized end # Returns the UNNORMALIZED value of this attribute. That is, entities @@ -117,8 +134,9 @@ doc = @element.document doctype = doc.doctype if doc end + @unnormalized = Text::unnormalize( @normalized, doctype ) @normalized = nil - @unnormalized = Text::unnormalize( @value, doctype ) + @unnormalized end # Returns a copy of this attribute