canon 0.3.20 → 0.3.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/canon/version.rb +1 -1
- data/lib/canon/xml/character_encoder.rb +12 -2
- data/lib/canon/xml/namespace_handler.rb +24 -5
- data/lib/canon/xml/nodes/element_node.rb +16 -5
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fd899c17d1512c815d189076fe08721dbe66b7d964f8bac69abde35f207b8c76
|
|
4
|
+
data.tar.gz: 205ad0b365288bf883f2ce57f68b612217151e30ae90fb0f959b29f252063826
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5aa4d552592cda5a55417415efb225151eb0d39d899fe0471eb26f10ceeca64ae56de786d8e807e0085264ba800cfd5937a6ec15e182e71957381414f3036105
|
|
7
|
+
data.tar.gz: b52ceb55e3ca6c6affcca3c7091a0e3f6e22d1c077b65732e7073915c3f6914cb86a61e675b11636baeb1ea09e94c223c933e961c0b366fb3e621c592a50170b
|
data/lib/canon/version.rb
CHANGED
|
@@ -5,10 +5,18 @@ module Canon
|
|
|
5
5
|
# Character encoder for C14N 1.1
|
|
6
6
|
# Handles UTF-8 encoding and character reference encoding per spec
|
|
7
7
|
class CharacterEncoder
|
|
8
|
+
# Most text and attribute values contain nothing to escape —
|
|
9
|
+
# the zero-allocation guard keeps those from paying the gsub
|
|
10
|
+
# copy on every render.
|
|
11
|
+
TEXT_ESCAPABLE = /[&<>\r]/
|
|
12
|
+
ATTRIBUTE_ESCAPABLE = /[&<"\t\n\r]/
|
|
13
|
+
|
|
8
14
|
# Encode text node content
|
|
9
15
|
# Replace: & → &, < → <, > → >, #xD → 
|
|
10
16
|
def encode_text(text)
|
|
11
|
-
text.
|
|
17
|
+
return text unless text.match?(TEXT_ESCAPABLE)
|
|
18
|
+
|
|
19
|
+
text.gsub(TEXT_ESCAPABLE) do |char|
|
|
12
20
|
case char
|
|
13
21
|
when "&" then "&"
|
|
14
22
|
when "<" then "<"
|
|
@@ -22,7 +30,9 @@ module Canon
|
|
|
22
30
|
# Replace: & → &, < → <, " → ",
|
|
23
31
|
# #x9 → 	, #xA → 
, #xD → 
|
|
24
32
|
def encode_attribute(value)
|
|
25
|
-
value.
|
|
33
|
+
return value unless value.match?(ATTRIBUTE_ESCAPABLE)
|
|
34
|
+
|
|
35
|
+
value.gsub(ATTRIBUTE_ESCAPABLE) do |char|
|
|
26
36
|
case char
|
|
27
37
|
when "&" then "&"
|
|
28
38
|
when "<" then "<"
|
|
@@ -14,7 +14,7 @@ module Canon
|
|
|
14
14
|
def process_namespaces(element, output, parent_element = nil)
|
|
15
15
|
return unless element.in_node_set?
|
|
16
16
|
|
|
17
|
-
namespaces = element
|
|
17
|
+
namespaces = renderable_namespaces(element)
|
|
18
18
|
|
|
19
19
|
# Check if we need to emit xmlns="" for empty default namespace
|
|
20
20
|
if should_emit_empty_default_namespace?(element, namespaces,
|
|
@@ -61,26 +61,45 @@ module Canon
|
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
# Check if a namespace node should be skipped
|
|
64
|
-
def should_skip_namespace?(ns,
|
|
64
|
+
def should_skip_namespace?(ns, element, parent_element)
|
|
65
65
|
# Skip xml namespace with standard URI
|
|
66
66
|
return true if ns.xml_namespace?
|
|
67
67
|
|
|
68
68
|
# Skip if an ancestor already declared this namespace
|
|
69
|
-
return true if namespace_declared_by_ancestor?(ns,
|
|
69
|
+
return true if namespace_declared_by_ancestor?(ns, element,
|
|
70
|
+
parent_element)
|
|
70
71
|
|
|
71
72
|
false
|
|
72
73
|
end
|
|
73
74
|
|
|
74
75
|
# Check if a namespace is already declared by an ancestor
|
|
75
|
-
def namespace_declared_by_ancestor?(ns, parent_element)
|
|
76
|
+
def namespace_declared_by_ancestor?(ns, element, parent_element)
|
|
76
77
|
return false unless parent_element
|
|
77
78
|
|
|
78
|
-
|
|
79
|
+
parent_nodes = parent_element.namespace_nodes
|
|
80
|
+
# Elements that declared nothing share their parent's scope
|
|
81
|
+
# object (TreeBuilder scope sharing), so identical arrays mean
|
|
82
|
+
# identical bindings — every namespace of this element is
|
|
83
|
+
# already declared above and will be skipped without a scan.
|
|
84
|
+
return true if parent_nodes.equal?(element.namespace_nodes)
|
|
85
|
+
|
|
86
|
+
parent_ns = parent_nodes.find do |parent_ns|
|
|
79
87
|
parent_ns.prefix == ns.prefix && parent_ns.in_node_set?
|
|
80
88
|
end
|
|
81
89
|
|
|
82
90
|
parent_ns && parent_ns.uri == ns.uri
|
|
83
91
|
end
|
|
92
|
+
|
|
93
|
+
# Sorted, in-node-set namespaces for one element. Undeclaring
|
|
94
|
+
# elements share one frozen array object, and node-set marking is
|
|
95
|
+
# fixed before processing runs, so the rendered form is cached
|
|
96
|
+
# per array identity for the lifetime of this handler (one
|
|
97
|
+
# canonicalization run).
|
|
98
|
+
def renderable_namespaces(element)
|
|
99
|
+
cache = (@renderable_namespaces ||= {}.compare_by_identity)
|
|
100
|
+
nodes = element.namespace_nodes
|
|
101
|
+
cache[nodes] ||= nodes.sort_by(&:local_name).select(&:in_node_set?)
|
|
102
|
+
end
|
|
84
103
|
end
|
|
85
104
|
end
|
|
86
105
|
end
|
|
@@ -53,15 +53,26 @@ module Canon
|
|
|
53
53
|
(@attribute_nodes ||= []) << attribute_node
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
# Get namespace nodes in sorted order (lexicographically by local name)
|
|
56
|
+
# Get namespace nodes in sorted order (lexicographically by local name).
|
|
57
57
|
def sorted_namespace_nodes
|
|
58
|
-
namespace_nodes
|
|
58
|
+
nodes = namespace_nodes
|
|
59
|
+
return nodes if nodes.empty?
|
|
60
|
+
|
|
61
|
+
nodes.sort_by(&:local_name)
|
|
59
62
|
end
|
|
60
63
|
|
|
61
|
-
# Get attribute nodes in sorted order (by namespace URI then local
|
|
64
|
+
# Get attribute nodes in sorted order (by namespace URI then local
|
|
65
|
+
# name). A comparator block instead of sort_by keys allocates no
|
|
66
|
+
# per-attribute key arrays; (uri, name) pairs are unique per
|
|
67
|
+
# element (duplicates are resolved at build), so the order is
|
|
68
|
+
# identical.
|
|
62
69
|
def sorted_attribute_nodes
|
|
63
|
-
|
|
64
|
-
|
|
70
|
+
attrs = attribute_nodes
|
|
71
|
+
return attrs if attrs.empty?
|
|
72
|
+
|
|
73
|
+
attrs.sort do |a, b|
|
|
74
|
+
(a.namespace_uri.to_s <=> b.namespace_uri.to_s).nonzero? ||
|
|
75
|
+
(a.local_name <=> b.local_name)
|
|
65
76
|
end
|
|
66
77
|
end
|
|
67
78
|
|