aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKouhei Sutou <kou@clear-code.com>2019-01-01 06:32:18 +0900
committerHiroshi SHIBATA <hsbt@ruby-lang.org>2019-08-04 11:54:27 +0900
commit0f18bc7fca0668aa2ffeb15106525526e4f018ba (patch)
tree19b366029204501871ca1ea9d186aeda2095555d
parent9b36f0a787dfb1a2a3875b827b455b5a9a2f2680 (diff)
downloadruby-0f18bc7fca0668aa2ffeb15106525526e4f018ba.tar.gz
[ruby/rexml] Fix attribute's default namespace behavior
NOTE: It's a backward incompatible change. If we have any serious problems with this change, we may revert this change. The XML namespace specification says the default namespace doesn't apply to attribute names but it does in REXML without this change: https://www.w3.org/TR/xml-names/#uniqAttrs > the default namespace does not apply to attribute names REXML reports a parse error for the following XML that is described as a valid XML in the XML nsmaspace specification without this change: <!-- http://www.w3.org is bound to n1 and is the default --> <x xmlns:n1="http://www.w3.org" xmlns="http://www.w3.org" > <good a="1" b="2" /> <good a="1" n1:a="2" /> </x> If attribute doesn't have prefix, the attribute should return "" for both #prefix and #namespace. https://github.com/ruby/rexml/commit/9e4fd552bc
-rw-r--r--lib/rexml/attribute.rb21
-rw-r--r--lib/rexml/element.rb4
-rw-r--r--lib/rexml/xpath_parser.rb10
-rw-r--r--test/rexml/test_core.rb50
4 files changed, 63 insertions, 22 deletions
diff --git a/lib/rexml/attribute.rb b/lib/rexml/attribute.rb
index 37671cf15f..8933a013a2 100644
--- a/lib/rexml/attribute.rb
+++ b/lib/rexml/attribute.rb
@@ -67,15 +67,11 @@ module REXML
# e.add_attribute( "nsa:a", "aval" )
# e.add_attribute( "b", "bval" )
# e.attributes.get_attribute( "a" ).prefix # -> "nsa"
- # e.attributes.get_attribute( "b" ).prefix # -> "elns"
+ # e.attributes.get_attribute( "b" ).prefix # -> ""
# a = Attribute.new( "x", "y" )
# a.prefix # -> ""
def prefix
- pf = super
- if pf == ""
- pf = @element.prefix if @element
- end
- pf
+ super
end
# Returns the namespace URL, if defined, or nil otherwise
@@ -87,9 +83,8 @@ module REXML
# e.attribute("ns:a").namespace # => "http://url"
# e.attribute("nsx:a").namespace # => nil
#
- # TODO: This method should always return nil for no namespace
- # attribute. Because the default namespace doesn't apply to
- # attribute name.
+ # This method always returns "" for no namespace attribute. Because
+ # the default namespace doesn't apply to attribute names.
#
# From https://www.w3.org/TR/xml-names/#uniqAttrs
#
@@ -99,10 +94,14 @@ module REXML
# e.add_namespace("", "http://example.com/")
# e.namespace # => "http://example.com/"
# e.add_attribute("a", "b")
- # e.attribute("a").namespace # => nil
+ # e.attribute("a").namespace # => ""
def namespace arg=nil
arg = prefix if arg.nil?
- @element.namespace arg
+ if arg == ""
+ ""
+ else
+ @element.namespace(arg)
+ end
end
# Returns true if other is an Attribute and has the same name and value,
diff --git a/lib/rexml/element.rb b/lib/rexml/element.rb
index de83ec3b51..c706a7c245 100644
--- a/lib/rexml/element.rb
+++ b/lib/rexml/element.rb
@@ -1133,8 +1133,8 @@ module REXML
elsif old_attr.prefix != value.prefix
# Check for conflicting namespaces
if value.prefix != "xmlns" and old_attr.prefix != "xmlns"
- old_namespace = @element.namespace(old_attr.prefix)
- new_namespace = @element.namespace(value.prefix)
+ old_namespace = old_attr.namespace
+ new_namespace = value.namespace
if old_namespace == new_namespace
raise ParseException.new(
"Namespace conflict in adding attribute \"#{value.name}\": "+
diff --git a/lib/rexml/xpath_parser.rb b/lib/rexml/xpath_parser.rb
index 47fa4ef84e..321bc481af 100644
--- a/lib/rexml/xpath_parser.rb
+++ b/lib/rexml/xpath_parser.rb
@@ -493,17 +493,11 @@ module REXML
if prefix.nil?
raw_node.name == name
elsif prefix.empty?
- # FIXME: This DOUBLES the time XPath searches take
- raw_node.name == name and
- raw_node.namespace == raw_node.element.namespace
+ raw_node.name == name and raw_node.namespace == ""
else
# FIXME: This DOUBLES the time XPath searches take
ns = get_namespace(raw_node.element, prefix)
- if ns.empty?
- raw_node.name == name and raw_node.prefix.empty?
- else
- raw_node.name == name and raw_node.namespace == ns
- end
+ raw_node.name == name and raw_node.namespace == ns
end
else
false
diff --git a/test/rexml/test_core.rb b/test/rexml/test_core.rb
index 46036d7f12..41e6e43540 100644
--- a/test/rexml/test_core.rb
+++ b/test/rexml/test_core.rb
@@ -1,4 +1,4 @@
-# coding: binary
+# -*- coding: utf-8 -*-
# frozen_string_literal: false
require_relative "rexml_test_utils"
@@ -116,6 +116,54 @@ module REXMLTests
name4='test4'/>).join(' '), e.to_s
end
+ def test_attribute_namespace_conflict
+ # https://www.w3.org/TR/xml-names/#uniqAttrs
+ message = <<-MESSAGE
+Duplicate attribute "a"
+Line: 4
+Position: 140
+Last 80 unconsumed characters:
+ MESSAGE
+ assert_raise_with_message(REXML::ParseException, message) do
+ Document.new(<<-XML)
+<!-- http://www.w3.org is bound to n1 and n2 -->
+<x xmlns:n1="http://www.w3.org"
+ xmlns:n2="http://www.w3.org" >
+ <bad a="1" a="2" />
+ <bad n1:a="1" n2:a="2" />
+</x>
+ XML
+ end
+ end
+
+ def test_attribute_default_namespace
+ # https://www.w3.org/TR/xml-names/#uniqAttrs
+ document = Document.new(<<-XML)
+<!-- http://www.w3.org is bound to n1 and is the default -->
+<x xmlns:n1="http://www.w3.org"
+ xmlns="http://www.w3.org" >
+ <good a="1" b="2" />
+ <good a="1" n1:a="2" />
+</x>
+ XML
+ attributes = document.root.elements.collect do |element|
+ element.attributes.each_attribute.collect do |attribute|
+ [attribute.prefix, attribute.namespace, attribute.name]
+ end
+ end
+ assert_equal([
+ [
+ ["", "", "a"],
+ ["", "", "b"],
+ ],
+ [
+ ["", "", "a"],
+ ["n1", "http://www.w3.org", "a"],
+ ],
+ ],
+ attributes)
+ end
+
def test_cdata
test = "The quick brown fox jumped
& < & < \" '