saxon 0.1.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.
@@ -0,0 +1,16 @@
1
+ require 'saxon/loader'
2
+
3
+ module Saxon
4
+ module S9API
5
+ def self.const_missing(name)
6
+ Saxon::Loader.load!
7
+ begin
8
+ const_get(name)
9
+ rescue NameError
10
+ msg = "uninitialized constant Saxon::S9API::#{name}"
11
+ e = NameError.new(msg, name)
12
+ raise e
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,143 @@
1
+ require 'saxon/s9api'
2
+ require 'stringio'
3
+
4
+ module Saxon
5
+ # Serialize XDM objects.
6
+ class Serializer
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
141
+ end
142
+ end
143
+
@@ -0,0 +1,187 @@
1
+ require 'java'
2
+ require 'saxon/jaxp'
3
+ require 'uri'
4
+ require 'pathname'
5
+
6
+ module Saxon
7
+ # Provides a wrapper around the JAXP StreamSource class Saxon uses to bring
8
+ # the XML bytestream in. Provides some extra methods to make handling closing
9
+ # the source and its inputstream after consumption more idiomatic
10
+ class Source
11
+ module Helpers
12
+ # Given a File, or IO object which will return either #path or
13
+ # #base_uri, return the #base_uri, if present, or the #path, if present, or
14
+ # nil
15
+ # @param [File, IO] io A File or IO
16
+ # object representing the input XML file or data, or a String containing
17
+ # the XML
18
+ # @return [String, nil] the path or URI from the IO (or nil if there is none)
19
+ def self.base_uri(io)
20
+ if io.respond_to?(:base_uri)
21
+ return io.base_uri.to_s
22
+ end
23
+ io.path if io.respond_to?(:path)
24
+ end
25
+
26
+ # Given a File or IO return a Java InputStream
27
+ # @param [File, IO, org.jruby.util.IOInputStream, java.io.InputStream]
28
+ # io input to be converted to an input stream
29
+ # @return [java.io.InputStream] the wrapped input
30
+ def self.inputstream(io)
31
+ case io
32
+ when org.jruby.util.IOInputStream, java.io.InputStream
33
+ io
34
+ else
35
+ io.to_inputstream if io.respond_to?(:read)
36
+ end
37
+ end
38
+
39
+ # Given a path return a Java File object
40
+ # @param [String, Pathname] path the path to the file
41
+ # @return [java.io.File] the Java File object
42
+ def self.file(path)
43
+ java.io.File.new(path.to_s)
44
+ end
45
+ end
46
+
47
+ PathChecker = ->(path) {
48
+ File.file?(path)
49
+ }
50
+ URIChecker = ->(uri) {
51
+ begin
52
+ URI.parse(uri)
53
+ true
54
+ rescue URI::InvalidURIError
55
+ false
56
+ end
57
+ }
58
+
59
+ # Generate a Saxon::Source given an IO-like
60
+ #
61
+ # @param [IO, File] io The IO-like containing XML to be parsed
62
+ # @param [Hash] opts
63
+ # @option opts [String] :base_uri The Base URI for the Source - an
64
+ # absolute URI or relative path that will be used to resolve relative
65
+ # URLs in the XML. Setting this will override any path or URI derived
66
+ # from the IO-like.
67
+ # @return [Saxon::Source] the Saxon::Source wrapping the input
68
+ def self.from_io(io, opts = {})
69
+ base_uri = opts.fetch(:base_uri) { Helpers.base_uri(io) }
70
+ inputstream = Helpers.inputstream(io)
71
+ stream_source = Saxon::JAXP::StreamSource.new(inputstream, base_uri)
72
+ new(stream_source, inputstream)
73
+ end
74
+
75
+ # Generate a Saxon::Source given a path to a file
76
+ #
77
+ # @param [String, Pathname] path The path to the XML file to be parsed
78
+ # @param [Hash] opts
79
+ # @option opts [String] :base_uri The Base URI for the Source - an
80
+ # absolute URI or relative path that will be used to resolve relative
81
+ # URLs in the XML. Setting this will override the file path.
82
+ # @return [Saxon::Source] the Saxon::Source wrapping the input
83
+ def self.from_path(path, opts = {})
84
+ stream_source = Saxon::JAXP::StreamSource.new(Helpers.file(path))
85
+ stream_source.setSystemId(opts[:base_uri]) if opts[:base_uri]
86
+ new(stream_source)
87
+ end
88
+
89
+ # Generate a Saxon::Source given a URI
90
+ #
91
+ # @param [String, URI] uri The URI to the XML file to be parsed
92
+ # @param [Hash] opts
93
+ # @option opts [String] :base_uri The Base URI for the Source - an
94
+ # absolute URI or relative path that will be used to resolve relative
95
+ # URLs in the XML. Setting this will override the given URI.
96
+ # @return [Saxon::Source] the Saxon::Source wrapping the input
97
+ def self.from_uri(uri, opts = {})
98
+ stream_source = Saxon::JAXP::StreamSource.new(uri.to_s)
99
+ stream_source.setSystemId(opts[:base_uri]) if opts[:base_uri]
100
+ new(stream_source)
101
+ end
102
+
103
+ # Generate a Saxon::Source given a string containing XML
104
+ #
105
+ # @param [String] string The string containing XML to be parsed
106
+ # @param [Hash] opts
107
+ # @option opts [String] :base_uri The Base URI for the Source - an
108
+ # absolute URI or relative path that will be used to resolve relative
109
+ # URLs in the XML. This will be nil unless set.
110
+ # @return [Saxon::Source] the Saxon::Source wrapping the input
111
+ def self.from_string(string, opts = {})
112
+ reader = java.io.StringReader.new(string)
113
+ stream_source = Saxon::JAXP::StreamSource.new(reader)
114
+ stream_source.setSystemId(opts[:base_uri]) if opts[:base_uri]
115
+ new(stream_source, reader)
116
+ end
117
+
118
+ def self.create(io_path_uri_or_string, opts = {})
119
+ case io_path_uri_or_string
120
+ when IO, File, java.io.InputStream
121
+ from_io(io_path_uri_or_string, opts)
122
+ when Pathname, PathChecker
123
+ from_file(io_path_uri_or_string, opts)
124
+ when URIChecker
125
+ from_uri(io_path_uri_or_string, opts)
126
+ else
127
+ from_string(io_path_uri_or_string, opts)
128
+ end
129
+ end
130
+
131
+ attr_reader :stream_source, :inputstream
132
+ private :stream_source, :inputstream
133
+
134
+ # @api private
135
+ # @param [java.xml.transform.stream.StreamSource] stream_source The Java JAXP StreamSource
136
+ # @param [java.io.InputStream, java.io.StringReader] inputstream The Java InputStream or StringReader
137
+ def initialize(stream_source, inputstream = nil)
138
+ @stream_source = stream_source
139
+ @inputstream = inputstream
140
+ @closed = false
141
+ end
142
+
143
+ # @return [String] The base URI of the Source
144
+ def base_uri
145
+ stream_source.getSystemId
146
+ end
147
+
148
+ # @param [String, URI] uri The URI to use as the Source's Base URI
149
+ # @return [String] The new base URI of the Source
150
+ def base_uri=(uri)
151
+ stream_source.setSystemId(uri.to_s)
152
+ base_uri
153
+ end
154
+
155
+ # Close the Source and its associated InputStream or Reader, allowing those
156
+ # resources to be freed.
157
+ # @return [TrueClass] Returns true
158
+ def close
159
+ inputstream.close
160
+ @closed = true
161
+ end
162
+
163
+ # @return [Boolean] Returns true if the source is closed, false otherwise
164
+ def closed?
165
+ @closed
166
+ end
167
+
168
+ # Yields itself and then closes itself. To be used by DocumentBuilders or
169
+ # other consumers, making it easy to ensure the source is closed after it
170
+ # has been consumed.
171
+ #
172
+ # @raise [Saxon::SourceClosedError] if the Source has already been closed
173
+ # @yield [source] Yields self to the block
174
+ def consume(&block)
175
+ raise SourceClosedError if closed?
176
+ block.call(self)
177
+ close
178
+ end
179
+
180
+ # @return [java.xml.transform.stream.StreamSource] The underlying JAXP StreamSource
181
+ def to_java
182
+ @stream_source
183
+ end
184
+ end
185
+
186
+ class SourceClosedError < Exception; end
187
+ end
@@ -0,0 +1,3 @@
1
+ module Saxon
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,48 @@
1
+ require_relative 'qname'
2
+
3
+ module Saxon
4
+ # An XPath Data Model Node object, representing an XML document, or an element or one of the other node chunks in the XDM.
5
+ class XdmAtomicValue
6
+ # convert a single Ruby value into an XdmAtomicValue
7
+ #
8
+ # @param value the value to convert
9
+ # @return [Saxon::XdmAtomicValue]
10
+ def self.create(value)
11
+ new(Saxon::S9API::XdmAtomicValue.new(value))
12
+ end
13
+
14
+ attr_reader :s9_xdm_atomic_value
15
+ private :s9_xdm_atomic_value
16
+
17
+ # @api private
18
+ def initialize(s9_xdm_atomic_value)
19
+ @s9_xdm_atomic_value = s9_xdm_atomic_value
20
+ end
21
+
22
+ # Return a {QName} representing the type of the value
23
+ #
24
+ # @return [Saxon::QName] the {QName} of the value's type
25
+ def type_name
26
+ @type_name ||= Saxon::QName.new(s9_xdm_atomic_value.getTypeName)
27
+ end
28
+
29
+ # @return [Saxon::S9API::XdmAtomicValue] The underlying Saxon Java XDM atomic value object.
30
+ def to_java
31
+ s9_xdm_atomic_value
32
+ end
33
+
34
+ # compares two {XdmAtomicValue}s using the underlying Saxon and XDM comparision rules
35
+ # @param other [Saxon::XdmAtomicValue]
36
+ # @return [Boolean]
37
+ def ==(other)
38
+ return false unless other.is_a?(XdmAtomicValue)
39
+ s9_xdm_atomic_value.equals(other.to_java)
40
+ end
41
+
42
+ alias_method :eql?, :==
43
+
44
+ def hash
45
+ @hash ||= s9_xdm_atomic_value.hashCode
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,65 @@
1
+ require_relative 'axis_iterator'
2
+
3
+ module Saxon
4
+ # An XPath Data Model Node object, representing an XML document, or an element or one of the other node chunks in the XDM.
5
+ class XdmNode
6
+ include Enumerable
7
+
8
+ attr_reader :s9_xdm_node
9
+ private :s9_xdm_node
10
+
11
+ # @api private
12
+ def initialize(s9_xdm_node)
13
+ @s9_xdm_node = s9_xdm_node
14
+ end
15
+
16
+ # @return [Saxon::S9API::XdmNode] The underlying Saxon Java XDM node object.
17
+ def to_java
18
+ @s9_xdm_node
19
+ end
20
+
21
+ def node_name
22
+ return @node_name if instance_variable_defined?(:@node_name)
23
+ node_name = s9_xdm_node.getNodeName
24
+ @node_name = node_name.nil? ? nil : Saxon::QName.new(node_name)
25
+ end
26
+
27
+ def node_kind
28
+ @node_kind ||= case s9_xdm_node.nodeKind
29
+ when Saxon::S9API::XdmNodeKind::ELEMENT
30
+ :element
31
+ when Saxon::S9API::XdmNodeKind::TEXT
32
+ :text
33
+ when Saxon::S9API::XdmNodeKind::ATTRIBUTE
34
+ :attribute
35
+ when Saxon::S9API::XdmNodeKind::NAMESPACE
36
+ :namespace
37
+ when Saxon::S9API::XdmNodeKind::COMMENT
38
+ :comment
39
+ when Saxon::S9API::XdmNodeKind::PROCESSING_INSTRUCTION
40
+ :processing_instruction
41
+ when Saxon::S9API::XdmNodeKind::DOCUMENT
42
+ :document
43
+ end
44
+ end
45
+
46
+ def ==(other)
47
+ return false unless other.is_a?(XdmNode)
48
+ s9_xdm_node.equals(other.to_java)
49
+ end
50
+
51
+ alias_method :eql?, :==
52
+
53
+ def hash
54
+ @hash ||= s9_xdm_node.hashCode
55
+ end
56
+
57
+ def each(&block)
58
+ axis_iterator(:child).each(&block)
59
+ end
60
+
61
+ def axis_iterator(axis)
62
+ AxisIterator.new(self, axis)
63
+ end
64
+ end
65
+ end