saxon-rb 0.4.0-java → 0.5.0-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 +429 -42
- data/Gemfile +2 -2
- data/README.md +317 -10
- data/Rakefile +237 -7
- data/lib/net/sf/saxon/Saxon-HE/{9.9.1-5/Saxon-HE-9.9.1-5.jar → 9.9.1-6/Saxon-HE-9.9.1-6.jar} +0 -0
- data/lib/saxon-rb.rb +1 -0
- data/lib/{saxon_jars.rb → saxon-rb_jars.rb} +2 -2
- data/lib/saxon.rb +13 -0
- data/lib/saxon/axis_iterator.rb +8 -1
- data/lib/saxon/configuration.rb +1 -0
- data/lib/saxon/item_type.rb +12 -17
- data/lib/saxon/item_type/lexical_string_conversion.rb +136 -58
- data/lib/saxon/item_type/value_to_ruby.rb +13 -0
- data/lib/saxon/loader.rb +4 -1
- data/lib/saxon/nokogiri.rb +78 -0
- data/lib/saxon/occurrence_indicator.rb +32 -3
- data/lib/saxon/processor.rb +32 -1
- data/lib/saxon/qname.rb +37 -2
- data/lib/saxon/s9api.rb +5 -0
- data/lib/saxon/sequence_type.rb +131 -0
- data/lib/saxon/source.rb +207 -71
- data/lib/saxon/version.rb +1 -1
- data/lib/saxon/xdm.rb +7 -0
- data/lib/saxon/xdm/array.rb +16 -0
- data/lib/saxon/xdm/atomic_value.rb +7 -1
- data/lib/saxon/xdm/empty_sequence.rb +13 -0
- data/lib/saxon/xdm/external_object.rb +1 -0
- data/lib/saxon/xdm/function_item.rb +1 -0
- data/lib/saxon/xdm/item.rb +7 -0
- data/lib/saxon/xdm/map.rb +38 -0
- data/lib/saxon/xdm/node.rb +19 -1
- data/lib/saxon/xdm/sequence_like.rb +15 -0
- data/lib/saxon/xdm/value.rb +21 -5
- data/lib/saxon/xpath.rb +9 -0
- data/lib/saxon/xpath/compiler.rb +36 -1
- data/lib/saxon/xpath/executable.rb +53 -28
- data/lib/saxon/xpath/static_context.rb +19 -39
- data/lib/saxon/xpath/variable_declaration.rb +16 -49
- data/lib/saxon/xslt.rb +12 -0
- data/lib/saxon/xslt/compiler.rb +75 -6
- data/lib/saxon/xslt/evaluation_context.rb +19 -3
- data/lib/saxon/xslt/executable.rb +204 -14
- data/saxon-rb.gemspec +1 -1
- metadata +9 -7
- data/saxon.gemspec +0 -30
@@ -5,11 +5,18 @@ module Saxon
|
|
5
5
|
# A collection of lamba-like objects for converting XDM::AtomicValues into
|
6
6
|
# appropriate Ruby values
|
7
7
|
module ValueToRuby
|
8
|
+
# Regexp patterns to assist in converting XDM typed values into Ruby
|
9
|
+
# native types
|
8
10
|
module Patterns
|
9
11
|
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/
|
10
12
|
end
|
11
13
|
|
14
|
+
# Helper class that converts XDM Date/Time strings into something Ruby's {Time} class can
|
15
|
+
# handle
|
12
16
|
class DateTimeConvertor
|
17
|
+
# Convert an XDM Date/Time value into a native Ruby {Time} object.
|
18
|
+
# @return [Time, String] the converted Time, or the original XDM lexical
|
19
|
+
# string if conversion isn't possible
|
13
20
|
def self.call(xdm_atomic_value)
|
14
21
|
new(xdm_atomic_value).convert
|
15
22
|
end
|
@@ -21,11 +28,15 @@ module Saxon
|
|
21
28
|
@match = ValueToRuby::Patterns::DATE_TIME.match(@timestring)
|
22
29
|
end
|
23
30
|
|
31
|
+
# Return a {Time} instance if possible, otherwise return the string value from the XDM.
|
32
|
+
# @return [Time, String] the converted Time, or the original string
|
24
33
|
def convert
|
25
34
|
return timestring if match.nil?
|
26
35
|
Time.new(*integer_parts, decimal_part, tz_part)
|
27
36
|
end
|
28
37
|
|
38
|
+
private
|
39
|
+
|
29
40
|
def integer_parts
|
30
41
|
match[1..5].map { |n| Integer(n, 10) }
|
31
42
|
end
|
@@ -40,6 +51,8 @@ module Saxon
|
|
40
51
|
end
|
41
52
|
end
|
42
53
|
|
54
|
+
# A collection of Lambdas (or objects responding to +#call+) for
|
55
|
+
# converting XDM typed values to native Ruby types.
|
43
56
|
module Convertors
|
44
57
|
INTEGER = INT = SHORT = LONG = UNSIGNED_INT = UNSIGNED_SHORT = UNSIGNED_LONG = \
|
45
58
|
POSITIVE_INTEGER = NON_POSITIVE_INTEGER = NEGATIVE_INTEGER = NON_NEGATIVE_INTEGER = ->(xdm_atomic_value) {
|
data/lib/saxon/loader.rb
CHANGED
@@ -6,8 +6,11 @@ module Saxon
|
|
6
6
|
module S9API
|
7
7
|
end
|
8
8
|
|
9
|
+
# The mechanism for adding the JARs for either the bundled Saxon HE, or an
|
10
|
+
# external Saxon HE/PE/EE version, into the CLASSPATH and requiring them.
|
9
11
|
module Loader
|
10
12
|
LOAD_SEMAPHORE = Mutex.new
|
13
|
+
private_constant :LOAD_SEMAPHORE
|
11
14
|
|
12
15
|
# Error raised if Saxon::Loader.load! is called but the path handed
|
13
16
|
# in does not exist or is not a directory
|
@@ -45,7 +48,7 @@ module Saxon
|
|
45
48
|
else
|
46
49
|
if jars_not_on_classpath?
|
47
50
|
if saxon_home.nil?
|
48
|
-
require '
|
51
|
+
require 'saxon-rb_jars'
|
49
52
|
else
|
50
53
|
saxon_home = Pathname.new(saxon_home)
|
51
54
|
raise NoJarsError, saxon_home unless saxon_home.directory?
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'saxon/xslt'
|
2
|
+
|
3
|
+
module Saxon
|
4
|
+
module XSLT
|
5
|
+
class Executable
|
6
|
+
# Provided for Nokogiri API compatibility. Cannot use many XSLT 2 and 3
|
7
|
+
# features as a result.
|
8
|
+
#
|
9
|
+
# Transform the input document by applying templates as in XSLT 1. All
|
10
|
+
# parameters will be interpreted as Strings
|
11
|
+
#
|
12
|
+
# @param doc_node [Saxon::XDM::Node] the document to transform
|
13
|
+
# @param params [Hash, Array] a Hash of param name => value, or an Array
|
14
|
+
# of param names and values of the form +['param', 'value', 'param2',
|
15
|
+
# 'value']+. The array must be of an even-numbered length
|
16
|
+
# @return [Saxon::XDM::Value] the result document
|
17
|
+
def transform(doc_node, params = {})
|
18
|
+
apply_templates(doc_node, v1_parameters(params)).xdm_value
|
19
|
+
end
|
20
|
+
|
21
|
+
# Provided for Nokogiri API compatibility. Cannot use many XSLT 2 and 3
|
22
|
+
# features as a result.
|
23
|
+
#
|
24
|
+
# Transform the input document by applying templates as in XSLT 1, and
|
25
|
+
# then serializing the result to a string using the serialization options
|
26
|
+
# set in the XSLT stylesheet's +<xsl:output/>+.
|
27
|
+
#
|
28
|
+
# @param doc_node [Saxon::XDM::Node] the document to transform
|
29
|
+
# @param params [Hash, Array] a Hash of param name => value, or an Array
|
30
|
+
# of param names and values of the form +['param', 'value', 'param2',
|
31
|
+
# 'value']+. The array must be of an even-numbered length
|
32
|
+
# @return [String] a serialization of the the result document
|
33
|
+
def apply_to(doc_node, params = {})
|
34
|
+
apply_templates(doc_node, v1_parameters(params)).to_s
|
35
|
+
end
|
36
|
+
|
37
|
+
# Provided for Nokogiri API compatibility. Cannot use many XSLT 2 and 3
|
38
|
+
# features as a result.
|
39
|
+
#
|
40
|
+
# Serialize the input document to a string using the serialization options
|
41
|
+
# set in the XSLT stylesheet's +<xsl:output/>+.
|
42
|
+
#
|
43
|
+
# @param doc_node [Saxon::XDM::Node] the document to serialize
|
44
|
+
# @return [String] a serialization of the the input document
|
45
|
+
def serialize(doc_node)
|
46
|
+
s9_transformer = @s9_xslt_executable.load30
|
47
|
+
serializer = Saxon::Serializer.new(s9_transformer.newSerializer)
|
48
|
+
serializer.serialize(doc_node.to_java)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def v1_parameters(params = [])
|
54
|
+
v1_params = v1_params_hash(params).map { |qname, value|
|
55
|
+
[Saxon::QName.resolve(qname), Saxon::XDM.AtomicValue(v1_param_value(value))]
|
56
|
+
}.to_h
|
57
|
+
return {} if v1_params.empty?
|
58
|
+
{global_parameters: v1_params}
|
59
|
+
end
|
60
|
+
|
61
|
+
def v1_params_hash(params = [])
|
62
|
+
return params if params.is_a?(Hash)
|
63
|
+
params.each_slice(2).map { |k,v|
|
64
|
+
raise ArgumentError.new("Odd number of values passed as params: #{params}") if v.nil?
|
65
|
+
[k, v]
|
66
|
+
}.to_h
|
67
|
+
end
|
68
|
+
|
69
|
+
def v1_param_value(value)
|
70
|
+
if /\A(['"]).+\1\z/.match(value)
|
71
|
+
value.slice(1..-2)
|
72
|
+
else
|
73
|
+
value
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -1,32 +1,61 @@
|
|
1
1
|
require_relative 's9api'
|
2
2
|
|
3
3
|
module Saxon
|
4
|
-
# Provides simple access to Saxon's OccurrenceIndicator constants,
|
5
|
-
#
|
4
|
+
# Provides simple access to Saxon's OccurrenceIndicator constants, for
|
5
|
+
# declaring restrictions on the cardinality (length) of a sequence when, for
|
6
|
+
# example, defining a variable's type
|
6
7
|
module OccurrenceIndicator
|
7
8
|
class << self
|
9
|
+
# One thing
|
8
10
|
def one
|
9
11
|
@one ||= Saxon::S9API::OccurrenceIndicator::ONE
|
10
12
|
end
|
11
13
|
|
14
|
+
# One or more things
|
12
15
|
def one_or_more
|
13
16
|
@one_or_more ||= Saxon::S9API::OccurrenceIndicator::ONE_OR_MORE
|
14
17
|
end
|
15
18
|
|
19
|
+
# no things (the empty sequence)
|
16
20
|
def zero
|
17
21
|
@zero ||= Saxon::S9API::OccurrenceIndicator::ZERO
|
18
22
|
end
|
19
23
|
|
24
|
+
# zero or more things
|
20
25
|
def zero_or_more
|
21
26
|
@zero_or_more ||= Saxon::S9API::OccurrenceIndicator::ZERO_OR_MORE
|
22
27
|
end
|
23
28
|
|
29
|
+
# an optional thing
|
24
30
|
def zero_or_one
|
25
31
|
@zero_or_one ||= Saxon::S9API::OccurrenceIndicator::ZERO_OR_ONE
|
26
32
|
end
|
27
33
|
|
34
|
+
# The list of valid occurence indicator names, as symbols. These
|
35
|
+
# correspond directly to the methods returning OccurrenceIndicators in
|
36
|
+
# this module.
|
37
|
+
#
|
38
|
+
# @return [Array<Symbol>] the indicator names
|
28
39
|
def indicator_names
|
29
|
-
|
40
|
+
# .refine gets added to modules that have methods, so it's not in
|
41
|
+
# Module's public_methods list
|
42
|
+
@indicator_names ||= (public_methods(false) - Module.public_methods - [:indicator_names, :get_indicator, :refine])
|
43
|
+
end
|
44
|
+
|
45
|
+
# Return an OccurrenceIndicator given a name as a symbol. Passes through
|
46
|
+
# existing OccurrenceIndicator instances: this method is primarily for API
|
47
|
+
# use, most people will find it easier to directly call one of the
|
48
|
+
# methods, as in +OccurrenceIndicator.one+ rather than
|
49
|
+
# +OccurrenceIndicator.get_indicator(:one)+.
|
50
|
+
#
|
51
|
+
# @param indicator_name [Symbol, Saxon::S9API::OccurrenceIndicator] the name of the OccurrenceIndicator to return
|
52
|
+
# @return [Saxon::S9API::OccurrenceIndicator] the OccurrenceIndicator
|
53
|
+
def get_indicator(indicator_name)
|
54
|
+
return indicator_name if indicator_name.is_a?(Saxon::S9API::OccurrenceIndicator)
|
55
|
+
unless indicator_names.include?(indicator_name)
|
56
|
+
raise ArgumentError, "#{indicator_name.inspect} is not a valid indicator name (one of #{indicator_names.map(&:inspect).join(', ')})"
|
57
|
+
end
|
58
|
+
OccurrenceIndicator.send(indicator_name)
|
30
59
|
end
|
31
60
|
end
|
32
61
|
end
|
data/lib/saxon/processor.rb
CHANGED
@@ -76,7 +76,7 @@ module Saxon
|
|
76
76
|
# <tt>Processor</tt>. Sharing <tt>XSLT::Compiler</tt>s across threads is
|
77
77
|
# fine as long as the static context is not changed.
|
78
78
|
#
|
79
|
-
# @yield An
|
79
|
+
# @yield An XSLT compiler DSL block, see {Saxon::XSLT::Compiler.create}
|
80
80
|
# @return [Saxon::XSLT::Compiler] a new XSLT compiler
|
81
81
|
def xslt_compiler(&block)
|
82
82
|
Saxon::XSLT::Compiler.create(self, &block)
|
@@ -98,5 +98,36 @@ module Saxon
|
|
98
98
|
def config
|
99
99
|
@config ||= Saxon::Configuration.create(self)
|
100
100
|
end
|
101
|
+
|
102
|
+
# Create a {DocumentBuilder} and construct a {Source} and parse some XML.
|
103
|
+
# The args are passed to {Saxon::Source.create}, and the returned {Source}
|
104
|
+
# is parsed using {DocumentBuilder#build}. If a {Source} is passed in, parse
|
105
|
+
# that. Any options in +opts+ will be ignored in that case.
|
106
|
+
#
|
107
|
+
# @param input [Saxon::Source, IO, File, String, Pathname, URI] the input to
|
108
|
+
# be turned into a {Source} and parsed.
|
109
|
+
# @param opts [Hash] for Source creation. See {Saxon::Source.create}.
|
110
|
+
# @return [Saxon::XDM::Node] the XML document
|
111
|
+
def XML(input, opts = {})
|
112
|
+
source = Source.create(input, opts)
|
113
|
+
document_builder.build(source)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Construct a {Source} containing an XSLT stylesheet, create an
|
117
|
+
# {XSLT::Compiler}, and compile the source, returning the {XSLT::Executable}
|
118
|
+
# produced. If a {Source} is passed as +input+, then it will be passed
|
119
|
+
# through to the compiler and any source-related options in +opts+ will be
|
120
|
+
# ignored.
|
121
|
+
#
|
122
|
+
# @param input [Saxon::Source, IO, File, String, Pathname, URI] the input to
|
123
|
+
# be turned into a {Source} and parsed.
|
124
|
+
# @param opts [Hash] for Source creation. See {Saxon::Source.create}.
|
125
|
+
# @yield the block is executed as an {XSLT::EvaluationContext::DSL} instance
|
126
|
+
# and applied to the compiler
|
127
|
+
# @return [Saxon::XSLT::Executable] the XSLT Executable
|
128
|
+
def XSLT(input, opts = {}, &block)
|
129
|
+
source = Source.create(input, opts)
|
130
|
+
xslt_compiler(&block).compile(source)
|
131
|
+
end
|
101
132
|
end
|
102
133
|
end
|
data/lib/saxon/qname.rb
CHANGED
@@ -3,16 +3,39 @@ require 'saxon/s9api'
|
|
3
3
|
module Saxon
|
4
4
|
# Represents QNames
|
5
5
|
class QName
|
6
|
+
# Create a {QName} from a Clark-notation string.
|
7
|
+
#
|
8
|
+
# Clark-notation for QNames uses +{}+ to delimit the namespace, so for a
|
9
|
+
# QName not in a namespace it's simply +local-name+, and for one in a
|
10
|
+
# namespace it's +{http://example.org/ns}local-name+
|
11
|
+
#
|
12
|
+
# @param clark_string [String] A QName in Clark notation.
|
13
|
+
# @return [Saxon::QName] A QName
|
6
14
|
def self.clark(clark_string)
|
7
15
|
s9_qname = Saxon::S9API::QName.fromClarkName(clark_string)
|
8
16
|
new(s9_qname)
|
9
17
|
end
|
10
18
|
|
19
|
+
# Create a {QName} from an Expanded QName string.
|
20
|
+
#
|
21
|
+
# Expanded QNames uses +Q{}+ to delimit the namespace, so for a
|
22
|
+
# QName not in a namespace it's simply +Q{}local-name+ (or +local-name}), and for one in a
|
23
|
+
# namespace it's +Q{http://example.org/ns}local-name+
|
24
|
+
#
|
25
|
+
# @param eqname_string [String] A QName in Expanded QName notation.
|
26
|
+
# @return [Saxon::QName] A QName
|
11
27
|
def self.eqname(eqname_string)
|
12
28
|
s9_qname = Saxon::S9API::QName.fromEQName(eqname_string)
|
13
29
|
new(s9_qname)
|
14
30
|
end
|
15
31
|
|
32
|
+
# Create a {QName} from prefix, uri, and local name options
|
33
|
+
#
|
34
|
+
# @param opts [Hash]
|
35
|
+
# @option [String] :prefix the namespace prefix to use (optional, requires +:uri+ passed too)
|
36
|
+
# @option [String] :uri the namespace URI to use (only required for QNames in a namespace)
|
37
|
+
# @option [String] :local_name the local-part of the QName. Required.
|
38
|
+
# @return [Saxon::QName] the QName
|
16
39
|
def self.create(opts = {})
|
17
40
|
prefix = opts[:prefix]
|
18
41
|
uri = opts[:uri]
|
@@ -32,7 +55,7 @@ module Saxon
|
|
32
55
|
# it's an instance of the underlying Saxon Java QName, it'll be wrapped
|
33
56
|
# into a {Saxon::QName}
|
34
57
|
#
|
35
|
-
# If the arg is a string, it's resolved by using {
|
58
|
+
# If the arg is a string, it's resolved by using {resolve_qname_string}
|
36
59
|
#
|
37
60
|
# @param qname_or_string [String, Symbol, Saxon::QName] the qname to resolve
|
38
61
|
# @param namespaces [Hash<String => String>] the set of namespaces as a hash of <tt>"prefix" => "namespace-uri"</tt>
|
@@ -48,7 +71,7 @@ module Saxon
|
|
48
71
|
end
|
49
72
|
end
|
50
73
|
|
51
|
-
# Resolve a QName string of the form
|
74
|
+
# Resolve a QName string of the form +"prefix:local-name"+ into a
|
52
75
|
# {Saxon::QName} by looking up the namespace URI in a hash of
|
53
76
|
# <tt>"prefix" => "namespace-uri"</tt>
|
54
77
|
#
|
@@ -110,12 +133,21 @@ module Saxon
|
|
110
133
|
@s9_qname.getEQName
|
111
134
|
end
|
112
135
|
|
136
|
+
# Compare this QName with another. They compare equal if they have same URI
|
137
|
+
# and local name. Prefix is ignored.
|
138
|
+
#
|
139
|
+
# @param other [Saxon::QName] the QName to compare against
|
140
|
+
# @return [Boolean] whether the two compare equal
|
113
141
|
def ==(other)
|
114
142
|
return false unless other.is_a?(QName)
|
115
143
|
s9_qname.equals(other.to_java)
|
116
144
|
end
|
117
145
|
alias_method :eql?, :==
|
118
146
|
|
147
|
+
# Compute a hash-code for this {QName}.
|
148
|
+
#
|
149
|
+
# Two {QNames}s with the same local name and URI will have the same hash code (and will compare using eql?).
|
150
|
+
# @see Object#hash
|
119
151
|
def hash
|
120
152
|
@hash ||= (local_name + uri).hash
|
121
153
|
end
|
@@ -134,6 +166,8 @@ module Saxon
|
|
134
166
|
s9_qname.to_s
|
135
167
|
end
|
136
168
|
|
169
|
+
# Returns a more detailed string representation of the object, showing
|
170
|
+
# prefix, uri, and local_name instance variables
|
137
171
|
def inspect
|
138
172
|
"<Saxon::QName @prefix=#{prefix} @uri=#{uri} @local_name=#{local_name}>"
|
139
173
|
end
|
@@ -145,6 +179,7 @@ module Saxon
|
|
145
179
|
@qname_string, @prefix = qname_string, prefix
|
146
180
|
end
|
147
181
|
|
182
|
+
# The error message reports the unbound prefix and complete QName
|
148
183
|
def to_s
|
149
184
|
"Namespace prefix ‘#{@prefix}’ for QName ‘#{@qname_string}’ is not bound to a URI"
|
150
185
|
end
|
data/lib/saxon/s9api.rb
CHANGED
@@ -3,8 +3,13 @@ require 'saxon/loader'
|
|
3
3
|
module Saxon
|
4
4
|
module S9API
|
5
5
|
CLASS_IMPORT_SEMAPHORE = Mutex.new
|
6
|
+
private_constant :CLASS_IMPORT_SEMAPHORE
|
6
7
|
|
7
8
|
class << self
|
9
|
+
# Override the +const_missing+ hook in {S9API} so that we can delay
|
10
|
+
# loading the Saxon JARs until the user has had a chance to set an
|
11
|
+
# alternate location for them, if they don't want to use the bundled Saxon
|
12
|
+
# HE
|
8
13
|
def const_missing(name)
|
9
14
|
Saxon::Loader.load!
|
10
15
|
begin
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require_relative './item_type'
|
2
|
+
require_relative './occurrence_indicator'
|
3
|
+
|
4
|
+
module Saxon
|
5
|
+
# Represents a type definition for an XDM Sequence: an {ItemType} plus an
|
6
|
+
# {OccurrenceIndicator} as a restriction on the cardinality (length) of the
|
7
|
+
# sequence. Used most often to define variables in XPath or XSLT, plus in
|
8
|
+
# extension function definition.
|
9
|
+
class SequenceType
|
10
|
+
class << self
|
11
|
+
# Generate a {SequenceType} from a type declaration string (see
|
12
|
+
# {.from_type_decl}), or some combination of type name/Ruby class (see
|
13
|
+
# {ItemType.get_type}), and an {OccurrenceIndicator} or symbol referencing
|
14
|
+
# an OccurenceIndicator (one of +:zero_or_more+, +:one_or_more+,
|
15
|
+
# +:zero_or_one+, or +:one+)
|
16
|
+
#
|
17
|
+
# @return [Saxon::SequenceType] the resulting SequenceType
|
18
|
+
def create(type_name, occurrence_indicator = nil)
|
19
|
+
case type_name
|
20
|
+
when SequenceType
|
21
|
+
return type_name
|
22
|
+
when S9API::SequenceType
|
23
|
+
return new(type_name)
|
24
|
+
else
|
25
|
+
check_for_complete_decl!(type_name, occurrence_indicator)
|
26
|
+
return from_type_decl(type_name) if type_name.is_a?(String) && occurrence_indicator.nil?
|
27
|
+
item_type = ItemType.get_type(type_name)
|
28
|
+
occurrence_indicator = OccurrenceIndicator.get_indicator(occurrence_indicator)
|
29
|
+
new(item_type, occurrence_indicator)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Generate a {SequenceType} from a declaration string following the rules
|
34
|
+
# of parameter and function +as=+ declarations in XSLT, like
|
35
|
+
# <tt><xsl:variable ... as="xs:string+"/></tt>
|
36
|
+
#
|
37
|
+
# @param type_decl [String] the declaration string
|
38
|
+
# @return [Saxon::SequenceType] the resulting SequenceType
|
39
|
+
def from_type_decl(type_decl)
|
40
|
+
occurence_char = type_decl[-1]
|
41
|
+
occurence = case occurence_char
|
42
|
+
when '?'
|
43
|
+
new(ItemType.get_type(type_decl[0..-2]), OccurrenceIndicator.zero_or_one)
|
44
|
+
when '+'
|
45
|
+
new(ItemType.get_type(type_decl[0..-2]), OccurrenceIndicator.one_or_more)
|
46
|
+
when '*'
|
47
|
+
new(ItemType.get_type(type_decl[0..-2]), OccurrenceIndicator.zero_or_more)
|
48
|
+
else
|
49
|
+
new(ItemType.get_type(type_decl), OccurrenceIndicator.one)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def check_for_complete_decl!(type_name, occurrence_indicator)
|
56
|
+
return true if occurrence_indicator.nil?
|
57
|
+
if type_name_is_complete_decl?(type_name)
|
58
|
+
raise ArgumentError, "Cannot pass a complete type declaration (#{type_name}) and an OccurrenceIndicator"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def type_name_is_complete_decl?(type_name)
|
63
|
+
!!(type_name.is_a?(String) && type_name.match(/[+*?]$/))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# @overload initialize(item_type, occurrence_indicator)
|
68
|
+
# creates new instance using item type and occurrence indicator
|
69
|
+
# @param item_type [Saxon::ItemType] the sequence's item type
|
70
|
+
# @param occurrence_indicator [net.sf.saxon.s9api.OccurrenceIndicator] the
|
71
|
+
# occurrence indicator (cardinality) of the sequence
|
72
|
+
# @overload initialize(s9_sequence_type)
|
73
|
+
# create a new instance by wrapping one of Saxon's underlying Java
|
74
|
+
# +SequenceType+s
|
75
|
+
# @param s9_sequence_type [net.sf.saxon.s9api.SequenceType]
|
76
|
+
# @return [Saxon::SequenceType] the new SequenceType
|
77
|
+
def initialize(item_type, occurrence_indicator = nil)
|
78
|
+
if occurrence_indicator.nil? && !item_type.is_a?(S9API::SequenceType)
|
79
|
+
raise ArgumentError, "Expected a Java s9api.SequenceType when handed a single argument, but got a #{item_type.class}"
|
80
|
+
end
|
81
|
+
|
82
|
+
if occurrence_indicator.nil?
|
83
|
+
@s9_sequence_type = item_type
|
84
|
+
else
|
85
|
+
@item_type = item_type
|
86
|
+
@occurrence_indicator = occurrence_indicator
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# @return [Saxon::ItemType] the type's ItemType
|
91
|
+
def item_type
|
92
|
+
@item_type ||= Saxon::ItemType.get_type(s9_sequence_type.getItemType)
|
93
|
+
end
|
94
|
+
|
95
|
+
# @return [net.sf.saxon.s9api.OccurrenceIndicator] the type's OccurrenceIndicator
|
96
|
+
def occurrence_indicator
|
97
|
+
@occurrence_indicator ||= s9_sequence_type.getOccurrenceIndicator
|
98
|
+
end
|
99
|
+
|
100
|
+
# @return [net.sf.saxon.s9api.SequenceType] the underlying Saxon SequenceType
|
101
|
+
def to_java
|
102
|
+
s9_sequence_type
|
103
|
+
end
|
104
|
+
|
105
|
+
# Compare equal with another SequenceType
|
106
|
+
# @param other [Saxon::SequenceType]
|
107
|
+
# @return [Boolean] the result of comparing +self+ and +other+
|
108
|
+
def ==(other)
|
109
|
+
return false if other.class != self.class
|
110
|
+
item_type == other.item_type && occurrence_indicator == other.occurrence_indicator
|
111
|
+
end
|
112
|
+
alias_method :eql?, :==
|
113
|
+
|
114
|
+
# Generated hash code for use as keys in Hashes
|
115
|
+
def hash
|
116
|
+
@hash ||= item_type.hash ^ occurrence_indicator.hash
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
def s9_sequence_type
|
122
|
+
@s9_sequence_type ||= S9API::SequenceType.makeSequenceType(item_type.to_java, occurrence_indicator.to_java)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Convenience wrapper for {SequenceType.create}
|
127
|
+
# @see SequenceType.create
|
128
|
+
def self.SequenceType(*args)
|
129
|
+
SequenceType.create(*args)
|
130
|
+
end
|
131
|
+
end
|