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
@@ -8,6 +8,7 @@ module Saxon
|
|
8
8
|
# Regexp patterns to assist in converting XDM typed values into Ruby
|
9
9
|
# native types
|
10
10
|
module Patterns
|
11
|
+
# @see https://www.w3.org/TR/xmlschema-2/#dateTime-lexical-representation
|
11
12
|
DATE_TIME = /\A(-?[0-9]{4,})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]+)?)(Z|[-+][0-9]{2}:[0-9]{2})?\z/
|
12
13
|
end
|
13
14
|
|
@@ -54,34 +55,45 @@ module Saxon
|
|
54
55
|
# A collection of Lambdas (or objects responding to +#call+) for
|
55
56
|
# converting XDM typed values to native Ruby types.
|
56
57
|
module Convertors
|
58
|
+
# @see https://www.w3.org/TR/xmlschema-2/#integer
|
57
59
|
INTEGER = INT = SHORT = LONG = UNSIGNED_INT = UNSIGNED_SHORT = UNSIGNED_LONG = \
|
58
60
|
POSITIVE_INTEGER = NON_POSITIVE_INTEGER = NEGATIVE_INTEGER = NON_NEGATIVE_INTEGER = ->(xdm_atomic_value) {
|
59
61
|
xdm_atomic_value.to_java.getValue
|
60
62
|
}
|
63
|
+
# @see https://www.w3.org/TR/xmlschema-2/#decimal
|
61
64
|
DECIMAL = ->(xdm_atomic_value) {
|
62
65
|
BigDecimal(xdm_atomic_value.to_s)
|
63
66
|
}
|
67
|
+
# @see https://www.w3.org/TR/xmlschema-2/#float
|
64
68
|
FLOAT = DOUBLE = ->(xdm_atomic_value) {
|
65
69
|
xdm_atomic_value.to_java.getValue
|
66
70
|
}
|
71
|
+
# @see https://www.w3.org/TR/xmlschema-2/#dateTime
|
67
72
|
DATE_TIME = DATE_TIME_STAMP = ValueToRuby::DateTimeConvertor
|
73
|
+
# @see https://www.w3.org/TR/xmlschema-2/#boolean
|
68
74
|
BOOLEAN = ->(xdm_atomic_value) {
|
69
75
|
xdm_atomic_value.to_java.getValue
|
70
76
|
}
|
77
|
+
# @see https://www.w3.org/TR/xmlschema-2/#NOTATION
|
78
|
+
# @see https://www.w3.org/TR/xmlschema-2/#QName
|
71
79
|
NOTATION = QNAME = ->(xdm_atomic_value) {
|
72
80
|
Saxon::QName.new(xdm_atomic_value.to_java.getQNameValue)
|
73
81
|
}
|
82
|
+
# @see https://www.w3.org/TR/xmlschema-2/#base64Binary
|
74
83
|
BASE64_BINARY = ->(xdm_atomic_value) {
|
75
84
|
Base64.decode64(xdm_atomic_value.to_s)
|
76
85
|
}
|
86
|
+
# @see https://www.w3.org/TR/xmlschema-2/#hexBinary
|
77
87
|
HEX_BINARY = ->(xdm_atomic_value) {
|
78
88
|
bytes = []
|
79
89
|
xdm_atomic_value.to_s.scan(/../) { |x| bytes << x.hex.chr(Encoding::ASCII_8BIT) }
|
80
90
|
bytes.join
|
81
91
|
}
|
92
|
+
# @see https://www.w3.org/TR/xmlschema-2/#byte
|
82
93
|
BYTE = ->(xdm_atomic_value) {
|
83
94
|
[xdm_atomic_value.to_java.getLongValue].pack('c')
|
84
95
|
}
|
96
|
+
# @see https://www.w3.org/TR/xmlschema-2/#unsignedByte
|
85
97
|
UNSIGNED_BYTE = ->(xdm_atomic_value) {
|
86
98
|
[xdm_atomic_value.to_java.getLongValue].pack('C')
|
87
99
|
}
|
data/lib/saxon/loader.rb
CHANGED
@@ -19,6 +19,7 @@ module Saxon
|
|
19
19
|
@path = path
|
20
20
|
end
|
21
21
|
|
22
|
+
# returns an error message including the missing path
|
22
23
|
def to_s
|
23
24
|
"The path ('#{@path}') you supplied for the Saxon .jar files doesn't exist, sorry"
|
24
25
|
end
|
@@ -31,65 +32,76 @@ module Saxon
|
|
31
32
|
@path = path
|
32
33
|
end
|
33
34
|
|
35
|
+
# returns an error message including the path looked in and jars we were looking for
|
34
36
|
def to_s
|
35
37
|
"One of saxon9he.jar, saxon9pe.jar, or saxon9ee.jar must be present in the path ('#{@path}') you supplied, sorry"
|
36
38
|
end
|
37
39
|
end
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
LOAD_SEMAPHORE.synchronize do
|
46
|
-
if Saxon::S9API.const_defined?(:Processor)
|
41
|
+
class << self
|
42
|
+
# Are the Saxon jars missing from the Classpath?
|
43
|
+
# @return [Boolean] true if the Jars are not on the Classpath
|
44
|
+
def jars_not_on_classpath?
|
45
|
+
begin
|
46
|
+
Java::net.sf.saxon.s9api.Processor
|
47
47
|
false
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
48
|
+
rescue
|
49
|
+
true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Are the Saxon JARs on the Classpath?
|
54
|
+
# @return [Boolean] whether the Jars are on the Classpath already
|
55
|
+
def jars_on_classpath?
|
56
|
+
!jars_not_on_classpath?
|
57
|
+
end
|
58
|
+
|
59
|
+
# @param saxon_home [String, Pathname] the path to the dir containing
|
60
|
+
# Saxon's .jars. Defaults to the vendored Saxon HE
|
61
|
+
# @return [true, false] Returns true if Saxon had not been loaded and
|
62
|
+
# is now, and false if Saxon was already loaded
|
63
|
+
def load!(saxon_home = nil)
|
64
|
+
return false if instance_variable_defined?(:@saxon_loaded) && @saxon_loaded
|
65
|
+
LOAD_SEMAPHORE.synchronize do
|
66
|
+
if Saxon::S9API.const_defined?(:Processor)
|
67
|
+
false
|
68
|
+
else
|
69
|
+
if jars_not_on_classpath?
|
70
|
+
if saxon_home.nil?
|
71
|
+
require 'saxon-rb_jars'
|
72
|
+
else
|
73
|
+
saxon_home = Pathname.new(saxon_home)
|
74
|
+
raise NoJarsError, saxon_home unless saxon_home.directory?
|
75
|
+
jars = [main_jar(saxon_home)].compact
|
76
|
+
raise MissingJarError if jars.empty?
|
77
|
+
jars += extra_jars(saxon_home)
|
58
78
|
|
59
|
-
|
79
|
+
add_jars_to_classpath!(saxon_home, jars)
|
80
|
+
end
|
60
81
|
end
|
61
|
-
end
|
62
82
|
|
63
|
-
|
64
|
-
|
83
|
+
@saxon_loaded = true
|
84
|
+
true
|
85
|
+
end
|
65
86
|
end
|
66
87
|
end
|
67
|
-
end
|
68
88
|
|
69
|
-
|
70
|
-
|
71
|
-
def self.main_jar(path)
|
72
|
-
['saxon9he.jar', 'saxon9pe.jar', 'saxon9ee.jar'].map { |jar| path.join(jar) }.find { |jar| jar.file? }
|
73
|
-
end
|
89
|
+
private
|
74
90
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
([icu] + optional).compact
|
79
|
-
end
|
91
|
+
def main_jar(path)
|
92
|
+
['saxon9he.jar', 'saxon9pe.jar', 'saxon9ee.jar'].map { |jar| path.join(jar) }.find { |jar| jar.file? }
|
93
|
+
end
|
80
94
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
rescue
|
86
|
-
true
|
95
|
+
def extra_jars(path)
|
96
|
+
optional = ['saxon9-unpack.jar', 'saxon9-sql.jar'].map { |jar| path.join(jar) }.select { |jar| jar.file? }
|
97
|
+
icu = path.children.find { |jar| jar.extname == '.jar' && !jar.basename.to_s.match(/^saxon-icu|^icu4j/).nil? }
|
98
|
+
([icu] + optional).compact
|
87
99
|
end
|
88
|
-
end
|
89
100
|
|
90
|
-
|
91
|
-
|
92
|
-
|
101
|
+
def add_jars_to_classpath!(saxon_home, jars)
|
102
|
+
jars.each do |jar|
|
103
|
+
$CLASSPATH << jar.to_s
|
104
|
+
end
|
93
105
|
end
|
94
106
|
end
|
95
107
|
end
|
data/lib/saxon/nokogiri.rb
CHANGED
@@ -44,7 +44,7 @@ module Saxon
|
|
44
44
|
# @return [String] a serialization of the the input document
|
45
45
|
def serialize(doc_node)
|
46
46
|
s9_transformer = @s9_xslt_executable.load30
|
47
|
-
serializer = Saxon::Serializer.new(s9_transformer.newSerializer)
|
47
|
+
serializer = Saxon::Serializer::Object.new(s9_transformer.newSerializer)
|
48
48
|
serializer.serialize(doc_node.to_java)
|
49
49
|
end
|
50
50
|
|
data/lib/saxon/processor.rb
CHANGED
@@ -4,6 +4,7 @@ require 'saxon/configuration'
|
|
4
4
|
require 'saxon/document_builder'
|
5
5
|
require 'saxon/xpath'
|
6
6
|
require 'saxon/xslt'
|
7
|
+
require 'saxon/serializer'
|
7
8
|
|
8
9
|
module Saxon
|
9
10
|
# Saxon::Processor wraps the S9API::Processor object. This is the object
|
@@ -45,12 +46,14 @@ module Saxon
|
|
45
46
|
@s9_processor = s9_processor
|
46
47
|
end
|
47
48
|
|
48
|
-
# Generate a new DocumentBuilder that uses this Processor.
|
49
|
-
#
|
49
|
+
# Generate a new DocumentBuilder that uses this Processor. Sharing
|
50
|
+
# DocumentBuilders across threads is not safe.
|
50
51
|
#
|
52
|
+
# @yield A DocumentBuilder configuration DSL block, see
|
53
|
+
# {Saxon::DocumentBuilder.create}
|
51
54
|
# @return [Saxon::DocumentBuilder] A new Saxon::DocumentBuilder
|
52
|
-
def document_builder
|
53
|
-
Saxon::DocumentBuilder.
|
55
|
+
def document_builder(&block)
|
56
|
+
Saxon::DocumentBuilder.create(self, &block)
|
54
57
|
end
|
55
58
|
|
56
59
|
# Declare custom collations for use by XSLT, XPath, and XQuery processors
|
@@ -82,6 +85,17 @@ module Saxon
|
|
82
85
|
Saxon::XSLT::Compiler.create(self, &block)
|
83
86
|
end
|
84
87
|
|
88
|
+
# Generate a new +Serializer+ for directly serializing XDM Values that uses
|
89
|
+
# this +Processor+. +Serializer+s are effectively one-shot objects, and
|
90
|
+
# shouldn't be reused.
|
91
|
+
#
|
92
|
+
# @yield the block passed will be called bound to the serializer instance. See
|
93
|
+
# {Saxon::Serializer::Object.create}
|
94
|
+
# @return [Saxon::Serializer::Object]
|
95
|
+
def serializer(&block)
|
96
|
+
Saxon::Serializer::Object.create(self, &block)
|
97
|
+
end
|
98
|
+
|
85
99
|
# @return [net.sf.saxon.s9api.Processor] The underlying Saxon processor
|
86
100
|
def to_java
|
87
101
|
@s9_processor
|
data/lib/saxon/serializer.rb
CHANGED
@@ -1,143 +1,9 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require_relative './serializer/destination.rb'
|
2
|
+
require_relative './serializer/object.rb'
|
3
3
|
|
4
4
|
module Saxon
|
5
5
|
# Serialize XDM objects.
|
6
|
-
|
7
|
-
# Manage access to the serialization properties of this serializer, with
|
8
|
-
# hash-like access.
|
9
|
-
#
|
10
|
-
# Properties can be set explicitly through this API, or via XSLT or XQuery
|
11
|
-
# serialization options like `<xsl:output>`.
|
12
|
-
#
|
13
|
-
# Properties set explicitly here will override properties set through the
|
14
|
-
# document like `<xsl:output>`.
|
15
|
-
class OutputProperties
|
16
|
-
# @api private
|
17
|
-
# Provides mapping between symbols and the underlying Saxon property
|
18
|
-
# instances
|
19
|
-
def self.output_properties
|
20
|
-
@output_properties ||= Hash[
|
21
|
-
Saxon::S9API::Serializer::Property.values.map { |property|
|
22
|
-
qname = property.getQName
|
23
|
-
key = [
|
24
|
-
qname.getPrefix,
|
25
|
-
qname.getLocalName.tr('-', '_')
|
26
|
-
].reject { |str| str == '' }.join('_').to_sym
|
27
|
-
[key, property]
|
28
|
-
}
|
29
|
-
]
|
30
|
-
end
|
31
|
-
|
32
|
-
attr_reader :s9_serializer
|
33
|
-
|
34
|
-
# @api private
|
35
|
-
def initialize(s9_serializer)
|
36
|
-
@s9_serializer = s9_serializer
|
37
|
-
end
|
38
|
-
|
39
|
-
# @param [Symbol, Saxon::S9API::Serializer::Property] property The property to fetch
|
40
|
-
def [](property)
|
41
|
-
s9_serializer.getOutputProperty(resolved_property(property))
|
42
|
-
end
|
43
|
-
|
44
|
-
# @param [Symbol, Saxon::S9API::Serializer::Property] property The property to set
|
45
|
-
# @param [String] value The string value of the property
|
46
|
-
def []=(property, value)
|
47
|
-
s9_serializer.setOutputProperty(resolved_property(property), value)
|
48
|
-
end
|
49
|
-
|
50
|
-
# @overload fetch(property)
|
51
|
-
# @param [Symbol, Saxon::S9API::Serializer::Property] property The property to fetch
|
52
|
-
# @overload fetch(property, default)
|
53
|
-
# @param property [Symbol, Saxon::S9API::Serializer::Property] The property to fetch
|
54
|
-
# @param default [Object] The value to return if the property is unset
|
55
|
-
def fetch(property, default = nil)
|
56
|
-
explicit_value = self[property]
|
57
|
-
if explicit_value.nil? && !default.nil?
|
58
|
-
default
|
59
|
-
else
|
60
|
-
explicit_value
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
private
|
65
|
-
def resolved_property(property_key)
|
66
|
-
case property_key
|
67
|
-
when Symbol
|
68
|
-
self.class.output_properties.fetch(property_key)
|
69
|
-
else
|
70
|
-
property_key
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
attr_reader :s9_serializer
|
76
|
-
private :s9_serializer
|
77
|
-
|
78
|
-
# @api private
|
79
|
-
def initialize(s9_serializer)
|
80
|
-
@s9_serializer = s9_serializer
|
81
|
-
end
|
82
|
-
|
83
|
-
# @return [Saxon::Serializer::OutputProperties] hash-like access to the Output Properties
|
84
|
-
def output_property
|
85
|
-
@output_property ||= OutputProperties.new(s9_serializer)
|
86
|
-
end
|
87
|
-
|
88
|
-
# @overload serialize(xdm_value, io)
|
89
|
-
# Serialize an XdmValue to an IO
|
90
|
-
# @param [Saxon::XdmValue] xdm_value The XdmValue to serialize
|
91
|
-
# @param [File, IO] io The IO to serialize to
|
92
|
-
# @return [nil]
|
93
|
-
# @overload serialize(xdm_value, path)
|
94
|
-
# Serialize an XdmValue to file <tt>path</tt>
|
95
|
-
# @param [Saxon::XdmValue] xdm_value The XdmValue to serialize
|
96
|
-
# @param [String, Pathname] path The path of the file to serialize to
|
97
|
-
# @return [nil]
|
98
|
-
# @overload serialize(xdm_value)
|
99
|
-
# Serialize an XdmValue to a String
|
100
|
-
# @param [Saxon::XdmValue] xdm_value The XdmValue to serialize
|
101
|
-
# @return [String] The serialized XdmValue
|
102
|
-
def serialize(xdm_value, io_or_path = nil)
|
103
|
-
case io_or_path
|
104
|
-
when nil
|
105
|
-
serialize_to_string(xdm_value)
|
106
|
-
when String, Pathname
|
107
|
-
serialize_to_file(xdm_value, io_or_path)
|
108
|
-
else
|
109
|
-
serialize_to_io(xdm_value, io_or_path)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
# @return [Saxon::S9API::Serializer] The underlying Saxon Serializer object
|
114
|
-
def to_java
|
115
|
-
s9_serializer
|
116
|
-
end
|
117
|
-
|
118
|
-
private
|
119
|
-
|
120
|
-
def serialize_to_io(xdm_value, io)
|
121
|
-
s9_serializer.setOutputStream(io.to_outputstream)
|
122
|
-
s9_serializer.serializeXdmValue(xdm_value.to_java)
|
123
|
-
nil
|
124
|
-
end
|
125
|
-
|
126
|
-
def serialize_to_string(xdm_value)
|
127
|
-
str_encoding = output_property.fetch(:encoding, Encoding.default_internal || Encoding.default_external)
|
128
|
-
StringIO.open { |io|
|
129
|
-
io.binmode
|
130
|
-
serialize_to_io(xdm_value, io)
|
131
|
-
io.string.force_encoding(str_encoding)
|
132
|
-
}
|
133
|
-
end
|
134
|
-
|
135
|
-
def serialize_to_file(xdm_value, path)
|
136
|
-
file = Java::JavaIO::File.new(path)
|
137
|
-
s9_serializer.setOutputFile(file)
|
138
|
-
s9_serializer.serializeXdmValue(xdm_value.to_java)
|
139
|
-
nil
|
140
|
-
end
|
6
|
+
module Serializer
|
141
7
|
end
|
142
8
|
end
|
143
9
|
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require_relative '../s9api'
|
2
|
+
require_relative './output_properties'
|
3
|
+
|
4
|
+
module Saxon
|
5
|
+
module Serializer
|
6
|
+
# A Saxon Serializer to be used as a Destination for a transformation rather
|
7
|
+
# than being directly called with existing XDM objects
|
8
|
+
class Destination
|
9
|
+
include OutputProperties
|
10
|
+
|
11
|
+
attr_reader :s9_serializer, :invocation_lambda
|
12
|
+
|
13
|
+
# @api private
|
14
|
+
def initialize(s9_serializer, invocation_lambda, &block)
|
15
|
+
@s9_serializer, @invocation_lambda = s9_serializer, invocation_lambda
|
16
|
+
if block_given?
|
17
|
+
instance_exec(&block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# @overload serialize(io)
|
22
|
+
# Serialize the transformation to an IO
|
23
|
+
# @param [File, IO] io The IO to serialize to
|
24
|
+
# @return [nil]
|
25
|
+
# @overload serialize(path)
|
26
|
+
# Serialize the transformation to file <tt>path</tt>
|
27
|
+
# @param [String, Pathname] path The path of the file to serialize to
|
28
|
+
# @return [nil]
|
29
|
+
# @overload serialize
|
30
|
+
# Serialize the transformation to a String
|
31
|
+
# @return [String] The serialized XdmValue
|
32
|
+
def serialize(io_or_path = nil)
|
33
|
+
case io_or_path
|
34
|
+
when nil
|
35
|
+
serialize_to_string
|
36
|
+
when String, Pathname
|
37
|
+
serialize_to_file(io_or_path)
|
38
|
+
else
|
39
|
+
serialize_to_io(io_or_path)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [Saxon::S9API::Serializer] The underlying Saxon Serializer object
|
44
|
+
def to_java
|
45
|
+
s9_serializer
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def run_transform!
|
51
|
+
invocation_lambda.call(self.to_java)
|
52
|
+
end
|
53
|
+
|
54
|
+
def serialize_to_string
|
55
|
+
str_encoding = output_property.fetch(:encoding, Encoding.default_internal || Encoding.default_external)
|
56
|
+
StringIO.open { |io|
|
57
|
+
io.binmode
|
58
|
+
set_output_io(io)
|
59
|
+
run_transform!
|
60
|
+
io.string.force_encoding(str_encoding)
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
def serialize_to_io(io)
|
65
|
+
set_output_io(io)
|
66
|
+
run_transform!
|
67
|
+
end
|
68
|
+
|
69
|
+
def set_output_io(io)
|
70
|
+
s9_serializer.setOutputStream(io.to_outputstream)
|
71
|
+
end
|
72
|
+
|
73
|
+
def serialize_to_file(path)
|
74
|
+
file = Java::JavaIO::File.new(path.to_s)
|
75
|
+
s9_serializer.setOutputFile(file)
|
76
|
+
run_transform!
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|