saxon-rb 0.4.0-java

Sign up to get free protection for your applications and to get access to all the features.
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