saxon-rb 0.7.0-java → 0.8.1-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 +623 -44
- data/.rspec-jar-loading +2 -0
- data/.ruby-version +1 -1
- data/Rakefile +10 -4
- data/lib/saxon-rb.rb +1 -1
- data/lib/saxon/item_type.rb +116 -71
- data/lib/saxon/jruby_bug_6197_workaround.rb +5 -0
- data/lib/saxon/loader.rb +54 -43
- data/lib/saxon/s9api.rb +17 -17
- data/lib/saxon/version.rb +2 -2
- data/lib/saxon/xdm/atomic_value.rb +13 -8
- metadata +15 -13
data/.rspec-jar-loading
ADDED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
jruby-9.2.
|
1
|
+
jruby-9.2.13.0
|
data/Rakefile
CHANGED
@@ -25,7 +25,7 @@ task :circleci do
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
def jruby_image_tags
|
28
|
-
%w{9.2.9.0 9.1.17.0 9.2.10.0-SNAPSHOT-latest}
|
28
|
+
%w{9.2.9.0 9.1.17.0 9.2.10.0 9.2.11.1 9.2.12.0 9.2.13.0-SNAPSHOT-latest}
|
29
29
|
end
|
30
30
|
|
31
31
|
def jdk_image_tags
|
@@ -51,7 +51,7 @@ task :circleci do
|
|
51
51
|
|
52
52
|
def codeclimate_jobs
|
53
53
|
(alt_saxon_urls.keys << nil).map { |alt_saxon_url|
|
54
|
-
["9.2.
|
54
|
+
["9.2.12.0", "8-jdk-slim", alt_saxon_url]
|
55
55
|
}
|
56
56
|
end
|
57
57
|
|
@@ -190,12 +190,18 @@ task :circleci do
|
|
190
190
|
def run_tests_step(opts)
|
191
191
|
command = [
|
192
192
|
"mkdir -p /tmp/test-results",
|
193
|
-
"bundle exec rspec spec --profile 10 --format RspecJunitFormatter --out /tmp/test-results/rspec.xml
|
193
|
+
"VERIFY_SAXON_LAZY_LOADING=1 bundle exec rspec spec/jar_loading_spec.rb --options .rspec-jar-loading --profile 10 --format RspecJunitFormatter --out /tmp/test-results/rspec-jar-loading.xml"
|
194
194
|
]
|
195
195
|
if opts.fetch(:run_codeclimate)
|
196
196
|
command.prepend("./cc-test-reporter before-build")
|
197
|
-
command.append("if [ $? -eq 0 ]; then ./cc-test-reporter format-coverage -t simplecov -o \"cc-coverage#{"-alt-saxon" if opts.fetch(:alt_saxon_url)}.json\"; fi")
|
197
|
+
command.append("if [ $? -eq 0 ]; then ./cc-test-reporter format-coverage -t simplecov -o \"cc-coverage-jar-loading#{"-alt-saxon" if opts.fetch(:alt_saxon_url)}.json\"; fi")
|
198
198
|
end
|
199
|
+
command.append("rm -rf coverage")
|
200
|
+
command.append("bundle exec rspec spec --profile 10 --format RspecJunitFormatter --out /tmp/test-results/rspec.xml --format progress")
|
201
|
+
if opts.fetch(:run_codeclimate)
|
202
|
+
command.append("if [ $? -eq 0 ]; then ./cc-test-reporter format-coverage -t simplecov -o \"cc-coverage-main#{"-alt-saxon" if opts.fetch(:alt_saxon_url)}.json\"; fi")
|
203
|
+
end
|
204
|
+
|
199
205
|
{
|
200
206
|
"run" => {
|
201
207
|
"name" => "Run the tests" + (opts.fetch(:run_codeclimate) ? ", and upload coverage data to Code Climate" : ""),
|
data/lib/saxon-rb.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
require 'saxon'
|
data/lib/saxon/item_type.rb
CHANGED
@@ -6,8 +6,49 @@ require_relative 'item_type/value_to_ruby'
|
|
6
6
|
module Saxon
|
7
7
|
# Represent XDM types abstractly
|
8
8
|
class ItemType
|
9
|
-
#
|
10
|
-
#
|
9
|
+
# lazy-loading Hash so we can avoid eager-loading saxon Jars, which prevents
|
10
|
+
# using external Saxon Jars unless the user is more careful than they should
|
11
|
+
# have to be.
|
12
|
+
class LazyReadOnlyHash
|
13
|
+
include Enumerable
|
14
|
+
|
15
|
+
attr_reader :loaded_hash, :load_mutex, :init_block
|
16
|
+
private :loaded_hash, :load_mutex, :init_block
|
17
|
+
|
18
|
+
def initialize(&init_block)
|
19
|
+
@init_block = init_block
|
20
|
+
@load_mutex = Mutex.new
|
21
|
+
@loaded_hash = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def [](key)
|
25
|
+
ensure_loaded!
|
26
|
+
loaded_hash[key]
|
27
|
+
end
|
28
|
+
|
29
|
+
def fetch(*args, &block)
|
30
|
+
ensure_loaded!
|
31
|
+
loaded_hash.fetch(*args, &block)
|
32
|
+
end
|
33
|
+
|
34
|
+
def each(&block)
|
35
|
+
ensure_loaded!
|
36
|
+
loaded_hash.each(&block)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def ensure_loaded!
|
42
|
+
return true unless loaded_hash.nil?
|
43
|
+
load_mutex.synchronize do
|
44
|
+
return true unless loaded_hash.nil?
|
45
|
+
@loaded_hash = init_block.call
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Error raised when a Ruby class has no equivalent XDM type to
|
51
|
+
# be converted into
|
11
52
|
class UnmappedRubyTypeError < StandardError
|
12
53
|
def initialize(class_name)
|
13
54
|
@class_name = class_name
|
@@ -54,85 +95,89 @@ module Saxon
|
|
54
95
|
}.freeze
|
55
96
|
|
56
97
|
# A mapping of QNames to XDM type constants
|
57
|
-
QNAME_MAPPING =
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
98
|
+
QNAME_MAPPING = LazyReadOnlyHash.new do
|
99
|
+
{
|
100
|
+
'anyAtomicType' => :ANY_ATOMIC_VALUE,
|
101
|
+
'anyURI' => :ANY_URI,
|
102
|
+
'base64Binary' => :BASE64_BINARY,
|
103
|
+
'boolean' => :BOOLEAN,
|
104
|
+
'byte' => :BYTE,
|
105
|
+
'date' => :DATE,
|
106
|
+
'dateTime' => :DATE_TIME,
|
107
|
+
'dateTimeStamp' => :DATE_TIME_STAMP,
|
108
|
+
'dayTimeDuration' => :DAY_TIME_DURATION,
|
109
|
+
'decimal' => :DECIMAL,
|
110
|
+
'double' => :DOUBLE,
|
111
|
+
'duration' => :DURATION,
|
112
|
+
'ENTITY' => :ENTITY,
|
113
|
+
'float' => :FLOAT,
|
114
|
+
'gDay' => :G_DAY,
|
115
|
+
'gMonth' => :G_MONTH,
|
116
|
+
'gMonthDay' => :G_MONTH_DAY,
|
117
|
+
'gYear' => :G_YEAR,
|
118
|
+
'gYearMonth' => :G_YEAR_MONTH,
|
119
|
+
'hexBinary' => :HEX_BINARY,
|
120
|
+
'ID' => :ID,
|
121
|
+
'IDREF' => :IDREF,
|
122
|
+
'int' => :INT,
|
123
|
+
'integer' => :INTEGER,
|
124
|
+
'language' => :LANGUAGE,
|
125
|
+
'long' => :LONG,
|
126
|
+
'Name' => :NAME,
|
127
|
+
'NCName' => :NCNAME,
|
128
|
+
'negativeInteger' => :NEGATIVE_INTEGER,
|
129
|
+
'NMTOKEN' => :NMTOKEN,
|
130
|
+
'nonNegativeInteger' => :NON_NEGATIVE_INTEGER,
|
131
|
+
'nonPositiveInteger' => :NON_POSITIVE_INTEGER,
|
132
|
+
'normalizedString' => :NORMALIZED_STRING,
|
133
|
+
'NOTATION' => :NOTATION,
|
134
|
+
'numeric' => :NUMERIC,
|
135
|
+
'positiveInteger' => :POSITIVE_INTEGER,
|
136
|
+
'QName' => :QNAME,
|
137
|
+
'short' => :SHORT,
|
138
|
+
'string' => :STRING,
|
139
|
+
'time' => :TIME,
|
140
|
+
'token' => :TOKEN,
|
141
|
+
'unsignedByte' => :UNSIGNED_BYTE,
|
142
|
+
'unsignedInt' => :UNSIGNED_INT,
|
143
|
+
'unsignedLong' => :UNSIGNED_LONG,
|
144
|
+
'unsignedShort' => :UNSIGNED_SHORT,
|
145
|
+
'untypedAtomic' => :UNTYPED_ATOMIC,
|
146
|
+
'yearMonthDuration' => :YEAR_MONTH_DURATION
|
147
|
+
}.map { |local_name, constant|
|
148
|
+
qname = Saxon::QName.create({
|
149
|
+
prefix: 'xs', uri: 'http://www.w3.org/2001/XMLSchema',
|
150
|
+
local_name: local_name
|
151
|
+
})
|
152
|
+
[qname, constant]
|
153
|
+
}.to_h.freeze
|
154
|
+
end
|
112
155
|
|
113
156
|
# A mapping of type names/QNames to XDM type constants
|
114
|
-
STR_MAPPING =
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
157
|
+
STR_MAPPING = LazyReadOnlyHash.new do
|
158
|
+
{
|
159
|
+
'array(*)' => :ANY_ARRAY,
|
160
|
+
'item()' => :ANY_ITEM,
|
161
|
+
'map(*)' => :ANY_MAP,
|
162
|
+
'node()' => :ANY_NODE
|
163
|
+
}.merge(
|
164
|
+
Hash[QNAME_MAPPING.map { |qname, v| [qname.to_s, v] }]
|
165
|
+
).freeze
|
166
|
+
end
|
122
167
|
|
123
168
|
# convertors to generate lexical strings for a given {ItemType}, as a hash keyed on the ItemType
|
124
|
-
ATOMIC_VALUE_LEXICAL_STRING_CONVERTORS =
|
169
|
+
ATOMIC_VALUE_LEXICAL_STRING_CONVERTORS = LazyReadOnlyHash.new do
|
125
170
|
LexicalStringConversion::Convertors.constants.map { |const|
|
126
171
|
[S9API::ItemType.const_get(const), LexicalStringConversion::Convertors.const_get(const)]
|
127
|
-
}
|
128
|
-
|
172
|
+
}.to_h.freeze
|
173
|
+
end
|
129
174
|
|
130
175
|
# convertors from {XDM::AtomicValue} to a ruby primitve value, as a hash keyed on the ItemType
|
131
|
-
ATOMIC_VALUE_TO_RUBY_CONVERTORS =
|
176
|
+
ATOMIC_VALUE_TO_RUBY_CONVERTORS = LazyReadOnlyHash.new do
|
132
177
|
ValueToRuby::Convertors.constants.map { |const|
|
133
178
|
[S9API::ItemType.const_get(const), ValueToRuby::Convertors.const_get(const)]
|
134
|
-
}
|
135
|
-
|
179
|
+
}.to_h.freeze
|
180
|
+
end
|
136
181
|
|
137
182
|
class << self
|
138
183
|
# Get an appropriate {ItemType} for a Ruby type or given a type name as a
|
data/lib/saxon/loader.rb
CHANGED
@@ -38,60 +38,71 @@ module Saxon
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
LOAD_SEMAPHORE.synchronize do
|
48
|
-
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
|
49
47
|
false
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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)
|
60
78
|
|
61
|
-
|
79
|
+
add_jars_to_classpath!(saxon_home, jars)
|
80
|
+
end
|
62
81
|
end
|
63
|
-
end
|
64
82
|
|
65
|
-
|
66
|
-
|
83
|
+
require_relative 'jruby_bug_6197_workaround'
|
84
|
+
@saxon_loaded = true
|
85
|
+
true
|
86
|
+
end
|
67
87
|
end
|
68
88
|
end
|
69
|
-
end
|
70
89
|
|
71
|
-
|
72
|
-
|
73
|
-
def self.main_jar(path)
|
74
|
-
['saxon9he.jar', 'saxon9pe.jar', 'saxon9ee.jar'].map { |jar| path.join(jar) }.find { |jar| jar.file? }
|
75
|
-
end
|
90
|
+
private
|
76
91
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
([icu] + optional).compact
|
81
|
-
end
|
92
|
+
def main_jar(path)
|
93
|
+
['saxon9he.jar', 'saxon9pe.jar', 'saxon9ee.jar'].map { |jar| path.join(jar) }.find { |jar| jar.file? }
|
94
|
+
end
|
82
95
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
rescue
|
88
|
-
true
|
96
|
+
def extra_jars(path)
|
97
|
+
optional = ['saxon9-unpack.jar', 'saxon9-sql.jar'].map { |jar| path.join(jar) }.select { |jar| jar.file? }
|
98
|
+
icu = path.children.find { |jar| jar.extname == '.jar' && !jar.basename.to_s.match(/^saxon-icu|^icu4j/).nil? }
|
99
|
+
([icu] + optional).compact
|
89
100
|
end
|
90
|
-
end
|
91
101
|
|
92
|
-
|
93
|
-
|
94
|
-
|
102
|
+
def add_jars_to_classpath!(saxon_home, jars)
|
103
|
+
jars.each do |jar|
|
104
|
+
$CLASSPATH << jar.to_s
|
105
|
+
end
|
95
106
|
end
|
96
107
|
end
|
97
108
|
end
|
data/lib/saxon/s9api.rb
CHANGED
@@ -11,28 +11,28 @@ module Saxon
|
|
11
11
|
# alternate location for them, if they don't want to use the bundled Saxon
|
12
12
|
# HE
|
13
13
|
def const_missing(name)
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
14
|
+
CLASS_IMPORT_SEMAPHORE.synchronize {
|
15
|
+
return const_get(name) if const_defined?(name)
|
16
|
+
Saxon::Loader.load!
|
17
|
+
begin
|
18
|
+
const_set(name, imported_classes.const_get(name))
|
19
|
+
rescue NameError
|
20
|
+
msg = "uninitialized constant Saxon::S9API::#{name}"
|
21
|
+
e = NameError.new(msg, name)
|
22
|
+
raise e
|
23
|
+
end
|
24
|
+
}
|
22
25
|
end
|
23
26
|
|
24
27
|
private
|
25
28
|
|
26
29
|
def imported_classes
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
java_import Java::net.sf.saxon.lib.ParseOptions
|
34
|
-
}
|
35
|
-
end
|
30
|
+
@imported_classes ||= Module.new {
|
31
|
+
include_package 'net.sf.saxon.s9api'
|
32
|
+
java_import Java::net.sf.saxon.Configuration
|
33
|
+
java_import Java::net.sf.saxon.lib.FeatureKeys
|
34
|
+
java_import Java::net.sf.saxon.lib.ParseOptions
|
35
|
+
}
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
data/lib/saxon/version.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
require 'saxon/
|
1
|
+
require 'saxon/version/library'
|
2
2
|
|
3
3
|
module Saxon
|
4
4
|
# Provides the saxon-rb and underlying Saxon library versions
|
5
5
|
module Version
|
6
6
|
# The version of the saxon-rb gem (not of Saxon itself)
|
7
|
-
WRAPPER = "0.
|
7
|
+
WRAPPER = "0.8.1"
|
8
8
|
end
|
9
9
|
end
|
@@ -36,12 +36,17 @@ module Saxon
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
# ItemType representing QNames
|
40
|
-
XS_QNAME = ItemType.get_type('xs:QName')
|
41
|
-
# ItemType representing NOTATION
|
42
|
-
XS_NOTATION = ItemType.get_type('xs:NOTATION')
|
43
|
-
|
44
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
|
+
|
45
50
|
# Convert a single Ruby value into an XDM::AtomicValue
|
46
51
|
#
|
47
52
|
# If no explicit {ItemType} is passed, the correct type is guessed based
|
@@ -71,8 +76,8 @@ module Saxon
|
|
71
76
|
|
72
77
|
item_type = ItemType.get_type(item_type)
|
73
78
|
|
74
|
-
return new(Saxon::S9API::XdmAtomicValue.new(value.to_java)) if item_type ==
|
75
|
-
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
|
76
81
|
|
77
82
|
value_lexical_string = item_type.lexical_string(value)
|
78
83
|
new(new_s9_xdm_atomic_value(value_lexical_string, item_type))
|
@@ -91,7 +96,7 @@ module Saxon
|
|
91
96
|
# @return [Saxon::XDM::AtomicValue]
|
92
97
|
def from_lexical_string(value, item_type)
|
93
98
|
item_type = ItemType.get_type(item_type)
|
94
|
-
raise CannotCreateQNameFromString if item_type ==
|
99
|
+
raise CannotCreateQNameFromString if item_type == xs_qname
|
95
100
|
new(new_s9_xdm_atomic_value(value.to_s, item_type))
|
96
101
|
end
|
97
102
|
|