saxon-rb 0.4.0-java

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.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +62 -0
  3. data/.gitignore +13 -0
  4. data/.rspec +3 -0
  5. data/.ruby-version +1 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/Gemfile +6 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +43 -0
  10. data/Rakefile +20 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/lib/net/sf/saxon/Saxon-HE/9.9.1-5/Saxon-HE-9.9.1-5.jar +0 -0
  14. data/lib/saxon.rb +6 -0
  15. data/lib/saxon/axis_iterator.rb +31 -0
  16. data/lib/saxon/configuration.rb +116 -0
  17. data/lib/saxon/document_builder.rb +28 -0
  18. data/lib/saxon/item_type.rb +290 -0
  19. data/lib/saxon/item_type/lexical_string_conversion.rb +383 -0
  20. data/lib/saxon/item_type/value_to_ruby.rb +78 -0
  21. data/lib/saxon/jaxp.rb +8 -0
  22. data/lib/saxon/loader.rb +93 -0
  23. data/lib/saxon/occurrence_indicator.rb +33 -0
  24. data/lib/saxon/parse_options.rb +127 -0
  25. data/lib/saxon/processor.rb +102 -0
  26. data/lib/saxon/qname.rb +153 -0
  27. data/lib/saxon/s9api.rb +34 -0
  28. data/lib/saxon/serializer.rb +143 -0
  29. data/lib/saxon/source.rb +187 -0
  30. data/lib/saxon/version.rb +3 -0
  31. data/lib/saxon/xdm.rb +35 -0
  32. data/lib/saxon/xdm/array.rb +77 -0
  33. data/lib/saxon/xdm/atomic_value.rb +173 -0
  34. data/lib/saxon/xdm/empty_sequence.rb +37 -0
  35. data/lib/saxon/xdm/external_object.rb +21 -0
  36. data/lib/saxon/xdm/function_item.rb +21 -0
  37. data/lib/saxon/xdm/item.rb +32 -0
  38. data/lib/saxon/xdm/map.rb +77 -0
  39. data/lib/saxon/xdm/node.rb +71 -0
  40. data/lib/saxon/xdm/sequence_like.rb +30 -0
  41. data/lib/saxon/xdm/value.rb +145 -0
  42. data/lib/saxon/xpath.rb +8 -0
  43. data/lib/saxon/xpath/compiler.rb +69 -0
  44. data/lib/saxon/xpath/executable.rb +58 -0
  45. data/lib/saxon/xpath/static_context.rb +161 -0
  46. data/lib/saxon/xpath/variable_declaration.rb +68 -0
  47. data/lib/saxon/xslt.rb +8 -0
  48. data/lib/saxon/xslt/compiler.rb +70 -0
  49. data/lib/saxon/xslt/evaluation_context.rb +165 -0
  50. data/lib/saxon/xslt/executable.rb +156 -0
  51. data/lib/saxon_jars.rb +10 -0
  52. data/saxon-rb.gemspec +39 -0
  53. data/saxon.gemspec +30 -0
  54. metadata +240 -0
@@ -0,0 +1,71 @@
1
+ require_relative '../axis_iterator'
2
+ require_relative '../s9api'
3
+ require_relative 'sequence_like'
4
+
5
+ module Saxon
6
+ module XDM
7
+ # An XPath Data Model Node object, representing an XML document, or an element or one of the other node chunks in the XDM.
8
+ class Node
9
+ include XDM::SequenceLike
10
+ include XDM::ItemSequenceLike
11
+ include Enumerable
12
+
13
+ attr_reader :s9_xdm_node
14
+ private :s9_xdm_node
15
+
16
+ # @api private
17
+ def initialize(s9_xdm_node)
18
+ @s9_xdm_node = s9_xdm_node
19
+ end
20
+
21
+ # @return [Saxon::S9API::XdmNode] The underlying Saxon Java XDM node object.
22
+ def to_java
23
+ @s9_xdm_node
24
+ end
25
+
26
+ def node_name
27
+ return @node_name if instance_variable_defined?(:@node_name)
28
+ node_name = s9_xdm_node.getNodeName
29
+ @node_name = node_name.nil? ? nil : Saxon::QName.new(node_name)
30
+ end
31
+
32
+ def node_kind
33
+ @node_kind ||= case s9_xdm_node.nodeKind
34
+ when Saxon::S9API::XdmNodeKind::ELEMENT
35
+ :element
36
+ when Saxon::S9API::XdmNodeKind::TEXT
37
+ :text
38
+ when Saxon::S9API::XdmNodeKind::ATTRIBUTE
39
+ :attribute
40
+ when Saxon::S9API::XdmNodeKind::NAMESPACE
41
+ :namespace
42
+ when Saxon::S9API::XdmNodeKind::COMMENT
43
+ :comment
44
+ when Saxon::S9API::XdmNodeKind::PROCESSING_INSTRUCTION
45
+ :processing_instruction
46
+ when Saxon::S9API::XdmNodeKind::DOCUMENT
47
+ :document
48
+ end
49
+ end
50
+
51
+ def ==(other)
52
+ return false unless other.is_a?(XDM::Node)
53
+ s9_xdm_node.equals(other.to_java)
54
+ end
55
+
56
+ alias_method :eql?, :==
57
+
58
+ def hash
59
+ @hash ||= s9_xdm_node.hashCode
60
+ end
61
+
62
+ def each(&block)
63
+ axis_iterator(:child).each(&block)
64
+ end
65
+
66
+ def axis_iterator(axis)
67
+ AxisIterator.new(self, axis)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,30 @@
1
+ module Saxon
2
+ module XDM
3
+ module SequenceLike
4
+ def sequence_enum
5
+ raise NotImplementedError
6
+ end
7
+
8
+ def sequence_size
9
+ raise NotImplementedError
10
+ end
11
+
12
+ def append(other)
13
+ XDM::Value.create([self, other])
14
+ end
15
+
16
+ alias_method :<<, :append
17
+ alias_method :+, :append
18
+ end
19
+
20
+ module ItemSequenceLike
21
+ def sequence_enum
22
+ [self].to_enum
23
+ end
24
+
25
+ def sequence_size
26
+ 1
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,145 @@
1
+ require_relative '../s9api'
2
+ require_relative 'sequence_like'
3
+
4
+ module Saxon
5
+ module XDM
6
+ # An XPath Data Model Value object, representing a Sequence.
7
+ class Value
8
+ include XDM::SequenceLike
9
+ include Enumerable
10
+
11
+ class << self
12
+ # Create a new XDM::Value sequence containing the items passed in as a Ruby enumerable.
13
+ #
14
+ # @param items [Enumerable] A list of members
15
+ # @return [Saxon::XDM::Value] The XDM value
16
+ def create(*items)
17
+ items = items.flatten
18
+ case items.size
19
+ when 0
20
+ XDM.EmptySequence()
21
+ when 1
22
+ if existing_value = maybe_xdm_value(items.first)
23
+ return existing_value
24
+ end
25
+ XDM.Item(items.first)
26
+ else
27
+ new(Saxon::S9API::XdmValue.new(wrap_items(items)))
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def maybe_xdm_value(item)
34
+ return item if item.is_a?(self)
35
+ return new(item) if item.instance_of?(Saxon::S9API::XdmValue)
36
+ false
37
+ end
38
+
39
+ def wrap_items(items)
40
+ result = []
41
+ items.map { |item|
42
+ wrap_item(item, result)
43
+ }
44
+ result
45
+ end
46
+
47
+ def wrap_item(item, result)
48
+ if item.respond_to?(:sequence_enum)
49
+ item.sequence_enum.each do |item|
50
+ result << item.to_java
51
+ end
52
+ elsif item.respond_to?(:each)
53
+ item.each do |item|
54
+ result << XDM.Item(item).to_java
55
+ end
56
+ else
57
+ result << XDM.Item(item).to_java
58
+ end
59
+ end
60
+ end
61
+
62
+ attr_reader :s9_xdm_value
63
+ private :s9_xdm_value
64
+
65
+ # @api private
66
+ def initialize(s9_xdm_value)
67
+ @s9_xdm_value = s9_xdm_value
68
+ end
69
+
70
+ # @return [Fixnum] The size of the sequence
71
+ def size
72
+ s9_xdm_value.size
73
+ end
74
+
75
+ # Calls the given block once for each Item in the sequence, passing that
76
+ # item as a parameter. Returns the value itself.
77
+ #
78
+ # If no block is given, an Enumerator is returned.
79
+ #
80
+ # @overload
81
+ # @yield [item] The current XDM Item
82
+ # @yieldparam item [Saxon::XDM::AtomicValue, Saxon::XDM::Node] the item.
83
+ def each(&block)
84
+ to_enum.each(&block)
85
+ end
86
+
87
+ # @return [Saxon::S9API::XdmValue] The underlying Saxon Java XDM valuee object.
88
+ def to_java
89
+ @s9_xdm_value
90
+ end
91
+
92
+ # Compare this XDM::Value with another. Currently this requires iterating
93
+ # across the sequence, and the other sequence and comparing each member
94
+ # with the corresponding member in the other sequence.
95
+ #
96
+ # @param other [Saxon::XDM::Value] The XDM::Value to be compare against
97
+ # @return [Boolean] whether the two XDM::Values are equal
98
+ def ==(other)
99
+ return false unless other.is_a?(XDM::Value)
100
+ return false unless other.size == size
101
+ not_okay = to_enum.zip(other.to_enum).find { |mine, theirs|
102
+ mine != theirs
103
+ }
104
+ not_okay.nil? || !not_okay
105
+ end
106
+
107
+ alias_method :eql?, :==
108
+
109
+ # The hash code for the XDM::Value
110
+ # @return [Fixnum] The hash code
111
+ def hash
112
+ @hash ||= to_a.hash
113
+ end
114
+
115
+ # Returns a lazy Enumerator over the sequence
116
+ # @return [Enumerator::Lazy] the enumerator
117
+ def to_enum
118
+ s9_xdm_value.enum_for(:each).lazy.map { |s9_xdm_item|
119
+ XDM.Item(s9_xdm_item)
120
+ }.each
121
+ end
122
+
123
+ alias_method :enum_for, :to_enum
124
+
125
+ def sequence_enum
126
+ to_enum
127
+ end
128
+
129
+ def sequence_size
130
+ s9_xdm_value.size
131
+ end
132
+ end
133
+
134
+ # Placeholder class for Saxon Items that we haven't gotten to yet
135
+ class XDM::UnhandledItem
136
+ def initialize(s9_xdm_item)
137
+ @s9_xdm_item = s9_xdm_item
138
+ end
139
+
140
+ def to_java
141
+ @s9_xdm_item
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,8 @@
1
+ require_relative './xpath/compiler'
2
+
3
+ module Saxon
4
+ # Classes for compiling, configuring, and executing XPath queries against
5
+ # XDM nodes or documents
6
+ module XPath
7
+ end
8
+ end
@@ -0,0 +1,69 @@
1
+ require 'forwardable'
2
+ require_relative './static_context'
3
+ require_relative './executable'
4
+
5
+ module Saxon
6
+ module XPath
7
+ # Compiles XPath expressions so they can be executed
8
+ class Compiler
9
+ # Create a new <tt>XPath::Compiler</tt> using the supplied Processor.
10
+ # Passing a block gives access to a DSL for setting up the compiler's
11
+ # static context.
12
+ #
13
+ # @param processor [Saxon::Processor] the {Saxon::Processor} to use
14
+ # @yield An XPath compiler DSL block
15
+ # @return [Saxon::XPath::Compiler] the new compiler instance
16
+ def self.create(processor, &block)
17
+ static_context = XPath::StaticContext.define(block)
18
+ new(processor.to_java, static_context)
19
+ end
20
+
21
+ extend Forwardable
22
+
23
+ attr_reader :static_context
24
+ private :static_context
25
+
26
+ # @api private
27
+ # @param s9_processor [net.sf.saxon.s9api.Processor] the Saxon
28
+ # <tt>Processor</tt> to wrap
29
+ # @param static_context [Saxon::XPath::StaticContext] the static context
30
+ # XPaths compiled using this compiler will have
31
+ def initialize(s9_processor, static_context)
32
+ @s9_processor, @static_context = s9_processor, static_context
33
+ end
34
+
35
+ def_delegators :static_context, :default_collation, :declared_namespaces, :declared_variables
36
+ # @!attribute [r] default_collation
37
+ # @return [String] the URI of the default declared collation
38
+ # @!attribute [r] declared_namespaces
39
+ # @return [Hash<String => String>] declared namespaces as prefix => URI hash
40
+ # @!attribute [r] declared_variables
41
+ # @return [Hash<Saxon::QName => Saxon::XPath::VariableDeclaration>] declared variables as QName => Declaration hash
42
+
43
+ # @param expression [String] the XPath expression to compile
44
+ # @return [Saxon::XPath::Executable] the executable query
45
+ def compile(expression)
46
+ Saxon::XPath::Executable.new(new_compiler.compile(expression), static_context)
47
+ end
48
+
49
+ def create(&block)
50
+ new_static_context = static_context.define(block)
51
+ self.class.new(@s9_processor, new_static_context)
52
+ end
53
+
54
+ private
55
+
56
+ def new_compiler
57
+ compiler = @s9_processor.newXPathCompiler
58
+ declared_namespaces.each do |prefix, uri|
59
+ compiler.declareNamespace(prefix, uri)
60
+ end
61
+ declared_variables.each do |_, decl|
62
+ compiler.declareVariable(*decl.compiler_args)
63
+ end
64
+ compiler.declareDefaultCollation(default_collation) unless default_collation.nil?
65
+ compiler
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,58 @@
1
+ require_relative '../xdm'
2
+
3
+ module Saxon
4
+ module XPath
5
+ # Represents a compiled XPath query ready to be executed
6
+ class Executable
7
+ # @return [XPath::StaticContext] the XPath's static context
8
+ attr_reader :static_context
9
+
10
+ # @api private
11
+ # @param s9_xpath_executable [net.sf.saxon.s9api.XPathExecutable] the
12
+ # Saxon compiled XPath object
13
+ # @param static_context [XPath::StaticContext] the XPath's static
14
+ # context
15
+ def initialize(s9_xpath_executable, static_context)
16
+ @s9_xpath_executable, @static_context = s9_xpath_executable, static_context
17
+ end
18
+
19
+ # Run the compiled query using a passed-in node as the context item.
20
+ # @param context_item [Saxon::XDM::Node] the context item node
21
+ # @return [Saxon::XPath::Result] the result of the query as an
22
+ # enumerable
23
+ def run(context_item, variables = {})
24
+ selector = to_java.load
25
+ selector.setContextItem(context_item.to_java)
26
+ variables.each do |qname_or_string, value|
27
+ selector.setVariable(static_context.resolve_variable_qname(qname_or_string).to_java, Saxon::XDM.Value(value).to_java)
28
+ end
29
+ Result.new(selector.iterator)
30
+ end
31
+
32
+ # @return [net.sf.saxon.s9api.XPathExecutable] the underlying Saxon
33
+ # <tt>XPathExecutable</tt>
34
+ def to_java
35
+ @s9_xpath_executable
36
+ end
37
+ end
38
+
39
+ # The result of executing an XPath query as an enumerable object
40
+ class Result
41
+ include Enumerable
42
+
43
+ # @api private
44
+ # @param result_iterator [java.util.Iterator] the result of calling
45
+ # <tt>#iterator</tt> on a Saxon <tt>XPathSelector</tt>
46
+ def initialize(result_iterator)
47
+ @result_iterator = result_iterator
48
+ end
49
+
50
+ # Yields <tt>XDM::Node</tt>s from the query result. If no block is passed,
51
+ # returns an <tt>Enumerator</tt>
52
+ # @yieldparam xdm_node [Saxon::XDM::Node] the name that is yielded
53
+ def each(&block)
54
+ @result_iterator.lazy.map { |s9_xdm_node| Saxon::XDM::Node.new(s9_xdm_node) }.each(&block)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,161 @@
1
+ require_relative '../qname'
2
+ require_relative './variable_declaration'
3
+ module Saxon
4
+ module XPath
5
+ # Raised when an attempt to declare a variable is made using a string for
6
+ # the qname with a namespace prefix that has not been declared in the
7
+ # context yet
8
+ class MissingVariableNamespaceError < StandardError
9
+ def initialize(variable_name, prefix)
10
+ @variable_name, @prefix = variable_name, prefix
11
+ end
12
+
13
+ def to_s
14
+ "Namespace prefix ‘#{@prefix}’ for variable name ‘#{@variable_name}’ is not bound to a URI"
15
+ end
16
+ end
17
+
18
+ # Represents the static context for a compiled XPath. {StaticContext}s are immutable.
19
+ class StaticContext
20
+ # methods used by both {StaticContext} and {StaticContext::DSL}
21
+ module Common
22
+ # @param args [Hash]
23
+ # @option args [Hash<Saxon::QName => Saxon::XPath::VariableDeclaration>] :declared_variables Hash of declared variables
24
+ # @option args [Hash<String => String>] :declared_namespaces Hash of namespace bindings prefix => URI
25
+ # @option args [Hash<String => java.text.Collator>] :declared_collations Hash of URI => Collator bindings
26
+ # @option args [String] :default_collation URI of the default collation
27
+ def initialize(args = {})
28
+ @declared_variables = args.fetch(:declared_variables, {}).freeze
29
+ @declared_namespaces = args.fetch(:declared_namespaces, {}).freeze
30
+ @default_collation = args.fetch(:default_collation, nil).freeze
31
+ end
32
+
33
+ # returns the context details in a hash suitable for initializing a new one
34
+ # @return [Hash<Symbol => Hash,null>] the args hash
35
+ def args_hash
36
+ {
37
+ declared_namespaces: @declared_namespaces,
38
+ declared_variables: @declared_variables,
39
+ default_collation: @default_collation
40
+ }
41
+ end
42
+ end
43
+
44
+ # Provides the hooks for constructing a {StaticContext} with a DSL.
45
+ # @api private
46
+ class DSL
47
+ include Common
48
+
49
+ # Create an instance based on the args hash, and execute the passed in Proc/lambda against it using <tt>#instance_exec</tt> and return a
50
+ # new {StaticContext} with the results
51
+ # @param block [Proc] a Proc/lambda (or <tt>to_proc</tt>'d containing DSL calls
52
+ # @return [Saxon::XPath::StaticContext]
53
+ def self.define(block, args = {})
54
+ dsl = new(args)
55
+ dsl.instance_exec(&block) unless block.nil?
56
+ StaticContext.new(dsl.args_hash)
57
+ end
58
+
59
+ # Set the default Collation to use. This should be one of the special
60
+ # collation URIs Saxon recognises, or one that has been registered
61
+ # using Saxon::Processor#declare_collations on the Processor that
62
+ # created the {XPath::Compiler} this context is for.
63
+ #
64
+ # @param collation_uri [String] The URI of the Collation to set as the default
65
+ def default_collation(collation_uri)
66
+ @default_collation = collation_uri
67
+ end
68
+
69
+ # Bind prefixes to namespace URIs
70
+ #
71
+ # @param namespaces [Hash{String, Symbol => String}]
72
+ def namespace(namespaces = {})
73
+ @declared_namespaces = @declared_namespaces.merge(namespaces.transform_keys(&:to_s)).freeze
74
+ end
75
+
76
+ # Declare a XPath variable's existence in the context
77
+ #
78
+ # @param qname [String, Saxon::QName] The name of the variable as
79
+ # explicit QName or prefix:name string form. The string form requires
80
+ # the namespace prefix to have already been declared with {#namespace}
81
+ # @param type [String, Hash{Symbol => String, Class}, null] The type of the
82
+ # variable, either as a string using the same form as an XSLT
83
+ # <tt>as=""</tt> type definition, or as a hash of one key/value where
84
+ # that key is a Symbol taken from {Saxon::OccurenceIndicator} and the
85
+ # value is either a Class that {Saxon::ItemType} can convert to its
86
+ # XDM equivalent (e.g. {::String}), or a string that {Saxon::ItemType}
87
+ # can parse into an XDM type (e.g. <tt>xs:string</tthat
88
+ # {Saxon::ItemType} can parse into an XDM type (e.g.
89
+ # <tt>xs:string</tt> or <tt>element()</tt>).
90
+ # If it's nil, then the default <tt>item()*</tt> – anything – type declaration is used
91
+ def variable(qname, type = nil)
92
+ qname = resolve_variable_qname(qname)
93
+ @declared_variables = @declared_variables.merge({
94
+ qname => resolve_variable_declaration(qname, type)
95
+ }).freeze
96
+ end
97
+
98
+ private
99
+
100
+ def resolve_variable_qname(qname_or_string)
101
+ Saxon::QName.resolve(qname_or_string, @declared_namespaces)
102
+ end
103
+
104
+ def resolve_variable_type_decl(type_decl)
105
+ case type_decl
106
+ when String
107
+ occurence_char = type_decl[-1]
108
+ occurence = case occurence_char
109
+ when '?'
110
+ {zero_or_one: type_decl[0..-2]}
111
+ when '+'
112
+ {one_or_more: type_decl[0..-2]}
113
+ when '*'
114
+ {zero_or_more: type_decl[0..-2]}
115
+ else
116
+ {one: type_decl}
117
+ end
118
+ when Hash
119
+ type_decl
120
+ end
121
+ end
122
+
123
+ def resolve_variable_declaration(qname, type)
124
+ args_hash = resolve_variable_type_decl(type) || {}
125
+ args_hash[:qname] = qname
126
+ Saxon::XPath::VariableDeclaration.new(args_hash)
127
+ end
128
+ end
129
+
130
+ include Common
131
+
132
+ # Executes the Proc/lambda passed in with a new instance of
133
+ # {StaticContext} as <tt>self</tt>, allowing the DSL methods to be
134
+ # called in a DSL-ish way
135
+ #
136
+ # @param block [Proc] the block of DSL calls to be executed
137
+ # @return [Saxon::XPath::StaticContext] the static context created by the block
138
+ def self.define(block)
139
+ DSL.define(block)
140
+ end
141
+
142
+ attr_reader :declared_variables, :declared_namespaces, :default_collation
143
+
144
+ # @return [Saxon::QName]
145
+ # @overload resolve_variable_qname(qname)
146
+ # returns the QName
147
+ # @param qname_or_string [Saxon::QName] the name as a QName
148
+ # @overload resolve_variable_qname(string)
149
+ # resolve the <tt>prefix:local_name</tt> string into a proper QName by
150
+ # looking up the prefix in the {#declared_namespaces}
151
+ # @param qname_or_string [String] the name as a string
152
+ def resolve_variable_qname(qname_or_string)
153
+ Saxon::QName.resolve(qname_or_string, declared_namespaces)
154
+ end
155
+
156
+ def define(block)
157
+ DSL.define(block, args_hash)
158
+ end
159
+ end
160
+ end
161
+ end