saxon-rb 0.5.0-java → 0.7.3-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +32 -2
- data/.rspec-jar-loading +2 -0
- data/.ruby-version +1 -1
- data/.yardopts +1 -0
- data/README.md +42 -1
- data/Rakefile +8 -2
- data/docs/templates/plugin.rb +73 -0
- data/lib/saxon-rb.rb +0 -1
- data/lib/saxon/configuration.rb +15 -13
- data/lib/saxon/document_builder.rb +216 -5
- data/lib/saxon/feature_flags.rb +11 -0
- data/lib/saxon/feature_flags/errors.rb +8 -0
- data/lib/saxon/feature_flags/helpers.rb +15 -0
- data/lib/saxon/feature_flags/version.rb +100 -0
- data/lib/saxon/item_type.rb +116 -71
- data/lib/saxon/item_type/lexical_string_conversion.rb +78 -1
- data/lib/saxon/item_type/value_to_ruby.rb +12 -0
- data/lib/saxon/loader.rb +55 -43
- data/lib/saxon/nokogiri.rb +1 -1
- data/lib/saxon/processor.rb +18 -4
- data/lib/saxon/serializer.rb +3 -137
- data/lib/saxon/serializer/destination.rb +80 -0
- data/lib/saxon/serializer/object.rb +93 -0
- data/lib/saxon/serializer/output_properties.rb +83 -0
- data/lib/saxon/version.rb +7 -1
- data/lib/saxon/version/library.rb +89 -0
- data/lib/saxon/xdm/atomic_value.rb +16 -9
- data/lib/saxon/xdm/node.rb +34 -3
- data/lib/saxon/xpath/compiler.rb +2 -2
- data/lib/saxon/xpath/static_context.rb +6 -1
- data/lib/saxon/xslt/evaluation_context.rb +11 -1
- data/lib/saxon/xslt/executable.rb +35 -48
- data/lib/saxon/xslt/invocation.rb +97 -0
- data/saxon-rb.gemspec +2 -2
- metadata +17 -6
@@ -0,0 +1,93 @@
|
|
1
|
+
require_relative '../s9api'
|
2
|
+
require_relative './output_properties'
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
module Saxon
|
6
|
+
module Serializer
|
7
|
+
# A Saxon Serializer to be used directly with XDM Values rather than being
|
8
|
+
# called as a Destination for a transformation.
|
9
|
+
class Object
|
10
|
+
# Create a serializer from the passed in +Processor+. When called with a
|
11
|
+
# block, the block will be executed via instance-exec so that output
|
12
|
+
# properties can be set, e.g.
|
13
|
+
#
|
14
|
+
# Serializer::Object.create(processor) {
|
15
|
+
# output_property[:indent] = 'yes'
|
16
|
+
# }
|
17
|
+
#
|
18
|
+
# @param processor [Saxon::Processor] the processor to create this
|
19
|
+
# +Serializer::Object+ from
|
20
|
+
# @yield the passed block bound via instance-exec to the new serializer
|
21
|
+
def self.create(processor, &block)
|
22
|
+
new(processor.to_java.newSerializer, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
include OutputProperties
|
26
|
+
|
27
|
+
attr_reader :s9_serializer
|
28
|
+
private :s9_serializer
|
29
|
+
|
30
|
+
# @api private
|
31
|
+
def initialize(s9_serializer, &block)
|
32
|
+
@s9_serializer = s9_serializer
|
33
|
+
if block_given?
|
34
|
+
instance_exec(&block)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# @overload serialize(xdm_value, io)
|
39
|
+
# Serialize an XdmValue to an IO
|
40
|
+
# @param [Saxon::XdmValue] xdm_value The XdmValue to serialize
|
41
|
+
# @param [File, IO] io The IO to serialize to
|
42
|
+
# @return [nil]
|
43
|
+
# @overload serialize(xdm_value, path)
|
44
|
+
# Serialize an XdmValue to file <tt>path</tt>
|
45
|
+
# @param [Saxon::XdmValue] xdm_value The XdmValue to serialize
|
46
|
+
# @param [String, Pathname] path The path of the file to serialize to
|
47
|
+
# @return [nil]
|
48
|
+
# @overload serialize(xdm_value)
|
49
|
+
# Serialize an XdmValue to a String
|
50
|
+
# @param [Saxon::XdmValue] xdm_value The XdmValue to serialize
|
51
|
+
# @return [String] The serialized XdmValue
|
52
|
+
def serialize(xdm_value, io_or_path = nil)
|
53
|
+
case io_or_path
|
54
|
+
when nil
|
55
|
+
serialize_to_string(xdm_value)
|
56
|
+
when String, Pathname
|
57
|
+
serialize_to_file(xdm_value, io_or_path)
|
58
|
+
else
|
59
|
+
serialize_to_io(xdm_value, io_or_path)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [Saxon::S9API::Serializer] The underlying Saxon Serializer object
|
64
|
+
def to_java
|
65
|
+
s9_serializer
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def serialize_to_io(xdm_value, io)
|
71
|
+
s9_serializer.setOutputStream(io.to_outputstream)
|
72
|
+
s9_serializer.serializeXdmValue(xdm_value.to_java)
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
|
76
|
+
def serialize_to_string(xdm_value)
|
77
|
+
str_encoding = output_property.fetch(:encoding, Encoding.default_internal || Encoding.default_external)
|
78
|
+
StringIO.open { |io|
|
79
|
+
io.binmode
|
80
|
+
serialize_to_io(xdm_value, io)
|
81
|
+
io.string.force_encoding(str_encoding)
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
def serialize_to_file(xdm_value, path)
|
86
|
+
file = Java::JavaIO::File.new(path.to_s)
|
87
|
+
s9_serializer.setOutputFile(file)
|
88
|
+
s9_serializer.serializeXdmValue(xdm_value.to_java)
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Saxon
|
2
|
+
module Serializer
|
3
|
+
# Manage access to the serialization properties of this serializer, with
|
4
|
+
# hash-like access.
|
5
|
+
#
|
6
|
+
# Properties can be set explicitly through this API, or via XSLT or XQuery
|
7
|
+
# serialization options like +<xsl:output>+.
|
8
|
+
#
|
9
|
+
# Properties set explicitly here will override properties set through the
|
10
|
+
# document by +<xsl:output>+.
|
11
|
+
module OutputProperties
|
12
|
+
# @return [Saxon::Serializer::OutputProperties] hash-like access to the Output Properties
|
13
|
+
def output_property
|
14
|
+
@output_property ||= OutputProperties::Accessor.new(s9_serializer)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @api private
|
18
|
+
#
|
19
|
+
# The private wrapper class that manages getting and setting output
|
20
|
+
# properties on a Serializer in an idiomatic Ruby-like way.
|
21
|
+
class Accessor
|
22
|
+
# @api private
|
23
|
+
# Provides mapping between symbols and the underlying Saxon property
|
24
|
+
# instances
|
25
|
+
def self.output_properties
|
26
|
+
@output_properties ||= Hash[
|
27
|
+
Saxon::S9API::Serializer::Property.values.map { |property|
|
28
|
+
qname = property.getQName
|
29
|
+
key = [
|
30
|
+
qname.getPrefix,
|
31
|
+
qname.getLocalName.tr('-', '_')
|
32
|
+
].reject { |str| str == '' }.join('_').to_sym
|
33
|
+
[key, property]
|
34
|
+
}
|
35
|
+
]
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_reader :s9_serializer
|
39
|
+
|
40
|
+
# @api private
|
41
|
+
def initialize(s9_serializer)
|
42
|
+
@s9_serializer = s9_serializer
|
43
|
+
end
|
44
|
+
|
45
|
+
# @param [Symbol, Saxon::S9API::Serializer::Property] property The property to fetch
|
46
|
+
def [](property)
|
47
|
+
s9_serializer.getOutputProperty(resolved_property(property))
|
48
|
+
end
|
49
|
+
|
50
|
+
# @param [Symbol, Saxon::S9API::Serializer::Property] property The property to set
|
51
|
+
# @param [String] value The string value of the property
|
52
|
+
def []=(property, value)
|
53
|
+
s9_serializer.setOutputProperty(resolved_property(property), value)
|
54
|
+
end
|
55
|
+
|
56
|
+
# @overload fetch(property)
|
57
|
+
# @param [Symbol, Saxon::S9API::Serializer::Property] property The property to fetch
|
58
|
+
# @overload fetch(property, default)
|
59
|
+
# @param property [Symbol, Saxon::S9API::Serializer::Property] The property to fetch
|
60
|
+
# @param default [Object] The value to return if the property is unset
|
61
|
+
def fetch(property, default = nil)
|
62
|
+
explicit_value = self[property]
|
63
|
+
if explicit_value.nil? && !default.nil?
|
64
|
+
default
|
65
|
+
else
|
66
|
+
explicit_value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def resolved_property(property_key)
|
73
|
+
case property_key
|
74
|
+
when Symbol
|
75
|
+
self.class.output_properties.fetch(property_key)
|
76
|
+
else
|
77
|
+
property_key
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/saxon/version.rb
CHANGED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'saxon/s9api'
|
2
|
+
|
3
|
+
module Saxon
|
4
|
+
module Version
|
5
|
+
# The version of the underlying Saxon library, which we need to discover at
|
6
|
+
# runtime based on what version is on the Classpath
|
7
|
+
class Library
|
8
|
+
# The loaded version of the Saxon Java library
|
9
|
+
#
|
10
|
+
# @return [Saxon::Version::Library] the version of the loaded library
|
11
|
+
def self.loaded_version
|
12
|
+
Saxon::Loader.load!
|
13
|
+
|
14
|
+
sv = Java::net.sf.saxon.Version
|
15
|
+
new(sv.getProductVersion, sv.getStructuredVersionNumber, sv.softwareEdition)
|
16
|
+
end
|
17
|
+
|
18
|
+
include Comparable
|
19
|
+
|
20
|
+
# @return [String] the version string (e.g. '9.9.1.6', '10.0')
|
21
|
+
attr_reader :version
|
22
|
+
# @return [String] the version components (e.g. <tt>[9, 9, 1, 6]</tt>, <tt>[10, 0]</tt>)
|
23
|
+
attr_reader :components
|
24
|
+
# @return [Symbol] the edition (+:he+, +:pe+, or +:ee+)
|
25
|
+
attr_reader :edition
|
26
|
+
|
27
|
+
# @param version [String] the version string
|
28
|
+
# @param components [Array<Integer>] the version components separated
|
29
|
+
# @param edition [String, Symbol] the name of the Saxon edition (e.g. +:he+, +'HE'+)
|
30
|
+
def initialize(version, components, edition)
|
31
|
+
@version = version.dup.freeze
|
32
|
+
@components = components.dup.freeze
|
33
|
+
@edition = edition.downcase.to_sym
|
34
|
+
end
|
35
|
+
|
36
|
+
# Comparison against another instance
|
37
|
+
#
|
38
|
+
# @param other [Saxon::Version::Library] the other version to compare against
|
39
|
+
# @return [Integer] -1 for less, 1 for greater, 0 for equal
|
40
|
+
def <=>(other)
|
41
|
+
return false unless other.is_a?(self.class)
|
42
|
+
|
43
|
+
n_components = [self.components.length, other.components.length].max
|
44
|
+
(0..(n_components - 1)).reduce(0, &comparator(other))
|
45
|
+
end
|
46
|
+
|
47
|
+
# Pessimistic comparison à la rubygems +~>+: do I satisfy the other
|
48
|
+
# version if considered as a pessimistic version constraint
|
49
|
+
#
|
50
|
+
# @param pessimistic_version [Saxon::Version::Library] the version to
|
51
|
+
# compare pessimistically
|
52
|
+
# @return [Boolean] do I satisfy the constraint?
|
53
|
+
def pessimistic_compare(pessimistic_version)
|
54
|
+
pessimistic_components = pessimistic_version.components
|
55
|
+
pessimistic_components = pessimistic_components + [0] if pessimistic_components.length == 1
|
56
|
+
locked = pessimistic_components[0..-2]
|
57
|
+
locked = locked.zip(components[0..locked.length])
|
58
|
+
variable = [pessimistic_components[-1], components[locked.length]]
|
59
|
+
|
60
|
+
locked_ok = locked.all? { |check, mine|
|
61
|
+
check == mine
|
62
|
+
}
|
63
|
+
|
64
|
+
return false unless locked_ok
|
65
|
+
|
66
|
+
check, mine = variable
|
67
|
+
mine >= check
|
68
|
+
end
|
69
|
+
|
70
|
+
# @return [String] the version string
|
71
|
+
def to_s
|
72
|
+
version
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def comparator(other)
|
78
|
+
->(cmp, i) {
|
79
|
+
return cmp unless cmp == 0
|
80
|
+
|
81
|
+
mine = self.components[i].nil? ? 0 : self.components[i]
|
82
|
+
theirs = other.components[i].nil? ? 0 : other.components[i]
|
83
|
+
|
84
|
+
mine <=> theirs
|
85
|
+
}
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -20,6 +20,7 @@ module Saxon
|
|
20
20
|
# them imlpicitly through the XDM::AtomicValue creation process doesn't really
|
21
21
|
# work. They need to be created explicitly and then handed in to be wrapped.
|
22
22
|
class CannotCreateQNameFromString < StandardError
|
23
|
+
# returns an error message
|
23
24
|
def to_s
|
24
25
|
"QName XDM::AtomicValues must be created using an instance of Saxon::QName, not a string like 'prefix:name': Prefix URI binding is undefined at this point"
|
25
26
|
end
|
@@ -29,17 +30,23 @@ module Saxon
|
|
29
30
|
# isn't a way to create these outside of parsing an XML document within
|
30
31
|
# Saxon, so attempting to do so raises this error.
|
31
32
|
class NotationCannotBeDirectlyCreated < StandardError
|
32
|
-
|
33
|
+
# returns an error message
|
34
|
+
def to_s
|
33
35
|
"xs:NOTATION XDM::AtomicValues cannot be directly created outside of XML parsing."
|
34
36
|
end
|
35
37
|
end
|
36
38
|
|
37
|
-
# ItemType representing QNames
|
38
|
-
XS_QNAME = ItemType.get_type('xs:QName')
|
39
|
-
# ItemType representing NOTATION
|
40
|
-
XS_NOTATION = ItemType.get_type('xs:NOTATION')
|
41
|
-
|
42
39
|
class << self
|
40
|
+
# ItemType representing QNames
|
41
|
+
def xs_qname
|
42
|
+
@xs_qname ||= ItemType.get_type('xs:QName')
|
43
|
+
end
|
44
|
+
|
45
|
+
# ItemType representing NOTATION
|
46
|
+
def xs_notation
|
47
|
+
@xs_notation ||= ItemType.get_type('xs:NOTATION')
|
48
|
+
end
|
49
|
+
|
43
50
|
# Convert a single Ruby value into an XDM::AtomicValue
|
44
51
|
#
|
45
52
|
# If no explicit {ItemType} is passed, the correct type is guessed based
|
@@ -69,8 +76,8 @@ module Saxon
|
|
69
76
|
|
70
77
|
item_type = ItemType.get_type(item_type)
|
71
78
|
|
72
|
-
return new(Saxon::S9API::XdmAtomicValue.new(value.to_java)) if item_type ==
|
73
|
-
raise NotationCannotBeDirectlyCreated if item_type ==
|
79
|
+
return new(Saxon::S9API::XdmAtomicValue.new(value.to_java)) if item_type == xs_qname && value_is_qname?(value)
|
80
|
+
raise NotationCannotBeDirectlyCreated if item_type == xs_notation
|
74
81
|
|
75
82
|
value_lexical_string = item_type.lexical_string(value)
|
76
83
|
new(new_s9_xdm_atomic_value(value_lexical_string, item_type))
|
@@ -89,7 +96,7 @@ module Saxon
|
|
89
96
|
# @return [Saxon::XDM::AtomicValue]
|
90
97
|
def from_lexical_string(value, item_type)
|
91
98
|
item_type = ItemType.get_type(item_type)
|
92
|
-
raise CannotCreateQNameFromString if item_type ==
|
99
|
+
raise CannotCreateQNameFromString if item_type == xs_qname
|
93
100
|
new(new_s9_xdm_atomic_value(value.to_s, item_type))
|
94
101
|
end
|
95
102
|
|
data/lib/saxon/xdm/node.rb
CHANGED
@@ -33,9 +33,10 @@ module Saxon
|
|
33
33
|
@node_name = node_name.nil? ? nil : Saxon::QName.new(node_name)
|
34
34
|
end
|
35
35
|
|
36
|
-
# What kind of node this is. Returns one of +:
|
37
|
-
# +:attribute+, +:namespace+, +:comment+,
|
38
|
-
# +:comment+
|
36
|
+
# What kind of node this is. Returns one of +:document+, +:element+,
|
37
|
+
# +:text+, +:attribute+, +:namespace+, +:comment+,
|
38
|
+
# +:processing_instruction+, or +:comment+
|
39
|
+
#
|
39
40
|
# @return [Symbol] the kind of node this is
|
40
41
|
def node_kind
|
41
42
|
@node_kind ||= case s9_xdm_node.nodeKind
|
@@ -84,6 +85,36 @@ module Saxon
|
|
84
85
|
def axis_iterator(axis)
|
85
86
|
AxisIterator.new(self, axis)
|
86
87
|
end
|
88
|
+
|
89
|
+
# Use Saxon's naive XDM Node serialisation to serialize the node and its
|
90
|
+
# descendants. Saxon uses a new Serializer with default options to
|
91
|
+
# serialize the node. In particular, if the Node was produced by an XSLT
|
92
|
+
# that used +<xsl:character-map>+ through +<xsl:output>+ to modify the
|
93
|
+
# contents, then they *WILL* *NOT* have been applied.
|
94
|
+
#
|
95
|
+
# +<xsl:output>+ has its effect at serialization time, not at XDM tree
|
96
|
+
# creation time, so it won't be applied until you serialize the document.
|
97
|
+
#
|
98
|
+
# Even then, unless you use a {Serializer} configured with the XSLT's
|
99
|
+
# +<xsl:output>+. We make a properly configured serializer available in
|
100
|
+
# the result of any XSLT transform (a {Saxon::XSLT::Invocation}), e.g.:
|
101
|
+
#
|
102
|
+
# result = xslt.apply_templates(input)
|
103
|
+
# result.to_s
|
104
|
+
# # or
|
105
|
+
# result.serialize('/path/to/output.xml')
|
106
|
+
#
|
107
|
+
# You can also get that {Serializer} directly from {XSLT::Executable},
|
108
|
+
# should you want to use the serialization options from a particular XSLT
|
109
|
+
# to serialize arbitrary XDM values:
|
110
|
+
#
|
111
|
+
# serializer = xslt.serializer
|
112
|
+
# serializer.serialize(an_xdm_value)
|
113
|
+
#
|
114
|
+
# @see http://www.saxonica.com/documentation9.9/index.html#!javadoc/net.sf.saxon.s9api/XdmNode@toString
|
115
|
+
def to_s
|
116
|
+
s9_xdm_node.toString
|
117
|
+
end
|
87
118
|
end
|
88
119
|
end
|
89
120
|
end
|
data/lib/saxon/xpath/compiler.rb
CHANGED
@@ -40,7 +40,7 @@ module Saxon
|
|
40
40
|
# static context.
|
41
41
|
#
|
42
42
|
# @param processor [Saxon::Processor] the {Saxon::Processor} to use
|
43
|
-
# @yield An XPath
|
43
|
+
# @yield An XPath::StaticContext::DSL block
|
44
44
|
# @return [Saxon::XPath::Compiler] the new compiler instance
|
45
45
|
def self.create(processor, &block)
|
46
46
|
static_context = XPath::StaticContext.define(block)
|
@@ -79,7 +79,7 @@ module Saxon
|
|
79
79
|
# Compiler's static context. As with {.create}, passing a block gives
|
80
80
|
# access to a DSL for setting up the compiler's static context.
|
81
81
|
#
|
82
|
-
# @yield An
|
82
|
+
# @yield An XPath::StaticContext::DSL block
|
83
83
|
# @return [Saxon::XPath::Compiler] the new compiler instance
|
84
84
|
def create(&block)
|
85
85
|
new_static_context = static_context.define(block)
|
@@ -117,7 +117,12 @@ module Saxon
|
|
117
117
|
DSL.define(block)
|
118
118
|
end
|
119
119
|
|
120
|
-
|
120
|
+
# @return [String] The default collation URI as a String
|
121
|
+
attr_reader :default_collation
|
122
|
+
# @return [Hash<Saxon::QName => Saxon::XPath::VariableDeclaration] the declared variables
|
123
|
+
attr_reader :declared_variables
|
124
|
+
# @return [Hash<String => String>] the declared namespaces, as a prefix => uri hash
|
125
|
+
attr_reader :declared_namespaces
|
121
126
|
|
122
127
|
# @return [Saxon::QName]
|
123
128
|
# @overload resolve_variable_qname(qname)
|
@@ -143,7 +143,17 @@ module Saxon
|
|
143
143
|
DSL.define(block)
|
144
144
|
end
|
145
145
|
|
146
|
-
|
146
|
+
|
147
|
+
# @return [String] The default collation URI as a String
|
148
|
+
attr_reader :default_collation
|
149
|
+
# @return [Hash<Saxon::QName => Saxon::XDM::Value>] All the static parameters
|
150
|
+
attr_reader :static_parameters
|
151
|
+
# @return [Hash<Saxon::QName => Saxon::XDM::Value>] All the global parameters
|
152
|
+
attr_reader :global_parameters
|
153
|
+
# @return [Hash<Saxon::QName => Saxon::XDM::Value>] All the initial template parameters
|
154
|
+
attr_reader :initial_template_parameters
|
155
|
+
# @return [Hash<Saxon::QName => Saxon::XDM::Value>] All the initial template parameters with tunnelling = "yes"
|
156
|
+
attr_reader :initial_template_tunnel_parameters
|
147
157
|
|
148
158
|
# @api private
|
149
159
|
# When passed a Proc, create a new EvaluationContext based on this one, with the same DSL available as in {.define}.
|
@@ -1,8 +1,10 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
require_relative 'evaluation_context'
|
3
|
+
require_relative 'invocation'
|
3
4
|
require_relative '../serializer'
|
4
5
|
require_relative '../xdm'
|
5
6
|
require_relative '../qname'
|
7
|
+
require_relative '../feature_flags'
|
6
8
|
|
7
9
|
module Saxon
|
8
10
|
module XSLT
|
@@ -52,6 +54,7 @@ module Saxon
|
|
52
54
|
# prefix, you must use an explicit {Saxon::QName} to refer to it.
|
53
55
|
class Executable
|
54
56
|
extend Forwardable
|
57
|
+
extend Saxon::FeatureFlags::Helpers
|
55
58
|
|
56
59
|
attr_reader :evaluation_context
|
57
60
|
private :evaluation_context
|
@@ -96,7 +99,7 @@ module Saxon
|
|
96
99
|
# to pass to the first template matched. Setting already-defined
|
97
100
|
# parameters will replace their value for this invocation of the XSLT
|
98
101
|
# only, it won't affect the {XSLT::Compiler}'s context.
|
99
|
-
# @return [Saxon::XSLT::
|
102
|
+
# @return [Saxon::XSLT::Invocation] the transformation result
|
100
103
|
def apply_templates(source, opts = {})
|
101
104
|
transformation(opts).apply_templates(source)
|
102
105
|
end
|
@@ -130,7 +133,7 @@ module Saxon
|
|
130
133
|
# to pass to the first template matched. Setting already-defined
|
131
134
|
# parameters will replace their value for this invocation of the XSLT
|
132
135
|
# only, it won't affect the {XSLT::Compiler}'s context.
|
133
|
-
# @return [Saxon::XSLT::
|
136
|
+
# @return [Saxon::XSLT::Invocation] the transformation result
|
134
137
|
def call_template(template_name = nil, opts = {})
|
135
138
|
transformation(opts).call_template(template_name)
|
136
139
|
end
|
@@ -155,14 +158,23 @@ module Saxon
|
|
155
158
|
# Additional global parameters to set. Setting already-defined
|
156
159
|
# parameters will replace their value for this invocation of the XSLT
|
157
160
|
# only, it won't affect the {XSLT::Compiler}'s context.
|
158
|
-
# @return [Saxon::XSLT::
|
161
|
+
# @return [Saxon::XSLT::Invocation] the transformation result
|
159
162
|
def call_function(function_name, opts = {})
|
160
163
|
args = opts.fetch(:args, [])
|
161
164
|
transformation(opts.reject { |k, v| k == :args }).call_function(function_name, args)
|
162
165
|
end
|
163
166
|
|
167
|
+
# Create a {Serializer::Object} configured using the options that were set
|
168
|
+
# by +<xsl:output>+.
|
169
|
+
#
|
170
|
+
# @return [Saxon::Serializer::Object] the Serializer
|
171
|
+
def serializer
|
172
|
+
Saxon::Serializer::Object.new(@s9_xslt_executable.load30.newSerializer)
|
173
|
+
end
|
174
|
+
requires_saxon_version :serializer, '>= 9.9'
|
175
|
+
|
164
176
|
# @return [net.sf.saxon.s9api.XsltExecutable] the underlying Saxon
|
165
|
-
#
|
177
|
+
# +XsltExecutable+
|
166
178
|
def to_java
|
167
179
|
@s9_xslt_executable
|
168
180
|
end
|
@@ -202,6 +214,7 @@ module Saxon
|
|
202
214
|
# Represents a loaded XSLT transformation ready to be applied against a
|
203
215
|
# context node.
|
204
216
|
class Transformation
|
217
|
+
# A list of valid option names for the transform
|
205
218
|
VALID_OPTS = [:raw, :mode, :global_context_item, :global_parameters, :initial_template_parameters, :initial_template_tunnel_parameters]
|
206
219
|
|
207
220
|
attr_reader :s9_transformer, :opts
|
@@ -213,6 +226,7 @@ module Saxon
|
|
213
226
|
@default_initial_template ||= Saxon::QName.clark('{http://www.w3.org/1999/XSL/Transform}initial-template')
|
214
227
|
end
|
215
228
|
|
229
|
+
# @api private
|
216
230
|
def initialize(args)
|
217
231
|
@s9_transformer = args.fetch(:s9_transformer)
|
218
232
|
@destination = args.fetch(:destination, nil)
|
@@ -225,57 +239,48 @@ module Saxon
|
|
225
239
|
# Apply templates to Source, using all the context set up when we were
|
226
240
|
# created.
|
227
241
|
def apply_templates(source)
|
228
|
-
|
242
|
+
transformation_invocation(:applyTemplates, source.to_java)
|
229
243
|
end
|
230
244
|
|
231
245
|
# Call the named template, using all the context set up when we were
|
232
246
|
# created.
|
233
247
|
def call_template(template_name)
|
234
|
-
|
248
|
+
transformation_invocation(:callTemplate, resolve_template_name(template_name))
|
235
249
|
end
|
236
250
|
|
237
251
|
# Call the named function, using all the context set up when we were
|
238
252
|
# created.
|
239
253
|
def call_function(function_name, args)
|
240
254
|
function_name = Saxon::QName.resolve(function_name).to_java
|
241
|
-
|
242
|
-
call_function_result(function_name, args)
|
255
|
+
transformation_invocation(:callFunction, function_name, function_args(args))
|
243
256
|
end
|
244
257
|
|
245
258
|
private
|
246
259
|
|
247
|
-
def
|
260
|
+
def transformation_invocation(invocation_method, *invocation_args)
|
248
261
|
set_opts!
|
249
|
-
|
250
|
-
Result.new(result_xdm_value(s9_transformer.send(*transformer_args)), s9_transformer)
|
262
|
+
XSLT::Invocation.new(s9_transformer, invocation_lambda(invocation_method, invocation_args), raw?)
|
251
263
|
end
|
252
264
|
|
253
|
-
def
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
)
|
265
|
+
def invocation_lambda(invocation_method, invocation_args)
|
266
|
+
->(destination) {
|
267
|
+
if destination.nil?
|
268
|
+
s9_transformer.send(invocation_method, *invocation_args)
|
269
|
+
else
|
270
|
+
s9_transformer.send(invocation_method, *invocation_args, destination.to_java)
|
271
|
+
end
|
272
|
+
}
|
262
273
|
end
|
263
274
|
|
264
275
|
def resolve_template_name(template_name)
|
265
|
-
return self.class.default_initial_template if template_name.nil?
|
266
|
-
Saxon::QName.resolve(template_name)
|
276
|
+
return self.class.default_initial_template.to_java if template_name.nil?
|
277
|
+
Saxon::QName.resolve(template_name).to_java
|
267
278
|
end
|
268
279
|
|
269
280
|
def function_args(args = [])
|
270
281
|
args.map { |val| Saxon::XDM.Value(val).to_java }.to_java(S9API::XdmValue)
|
271
282
|
end
|
272
283
|
|
273
|
-
def destination
|
274
|
-
@destination ||= begin
|
275
|
-
Saxon::S9API::XdmDestination.new unless raw?
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
284
|
def set_opts!
|
280
285
|
opts.each do |opt, value|
|
281
286
|
raise BadOptionError, opt unless VALID_OPTS.include?(opt)
|
@@ -304,29 +309,11 @@ module Saxon
|
|
304
309
|
end
|
305
310
|
|
306
311
|
def initial_template_parameters(parameters)
|
307
|
-
s9_transformer.setInitialTemplateParameters(XSLT::ParameterHelper.to_java(parameters)
|
312
|
+
s9_transformer.setInitialTemplateParameters(XSLT::ParameterHelper.to_java(parameters), false)
|
308
313
|
end
|
309
314
|
|
310
315
|
def initial_template_tunnel_parameters(parameters)
|
311
|
-
s9_transformer.setInitialTemplateParameters(XSLT::ParameterHelper.to_java(parameters)
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
315
|
-
# Represents the result of a transformation, providing a simple default
|
316
|
-
# serializer as well
|
317
|
-
class Result
|
318
|
-
attr_reader :xdm_value
|
319
|
-
|
320
|
-
# @api private
|
321
|
-
def initialize(xdm_value, s9_transformer)
|
322
|
-
@xdm_value, @s9_transformer = xdm_value, s9_transformer
|
323
|
-
end
|
324
|
-
|
325
|
-
# Serialize the result to a string using the options specified in
|
326
|
-
# +<xsl:output/>+ in the XSLT
|
327
|
-
def to_s
|
328
|
-
serializer = Serializer.new(@s9_transformer.newSerializer)
|
329
|
-
serializer.serialize(xdm_value.to_java)
|
316
|
+
s9_transformer.setInitialTemplateParameters(XSLT::ParameterHelper.to_java(parameters), true)
|
330
317
|
end
|
331
318
|
end
|
332
319
|
|