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,78 @@
1
+ require 'bigdecimal'
2
+
3
+ module Saxon
4
+ class ItemType
5
+ # A collection of lamba-like objects for converting XDM::AtomicValues into
6
+ # appropriate Ruby values
7
+ module ValueToRuby
8
+ module Patterns
9
+ 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
+ end
11
+
12
+ class DateTimeConvertor
13
+ def self.call(xdm_atomic_value)
14
+ new(xdm_atomic_value).convert
15
+ end
16
+
17
+ attr_reader :timestring, :match
18
+
19
+ def initialize(xdm_atomic_value)
20
+ @timestring = xdm_atomic_value.to_s
21
+ @match = ValueToRuby::Patterns::DATE_TIME.match(@timestring)
22
+ end
23
+
24
+ def convert
25
+ return timestring if match.nil?
26
+ Time.new(*integer_parts, decimal_part, tz_part)
27
+ end
28
+
29
+ def integer_parts
30
+ match[1..5].map { |n| Integer(n, 10) }
31
+ end
32
+
33
+ def decimal_part
34
+ Float(match[6])
35
+ end
36
+
37
+ def tz_part
38
+ tz_string = match[7]
39
+ tz_string == 'Z' ? '+00:00' : tz_string
40
+ end
41
+ end
42
+
43
+ module Convertors
44
+ INTEGER = INT = SHORT = LONG = UNSIGNED_INT = UNSIGNED_SHORT = UNSIGNED_LONG = \
45
+ POSITIVE_INTEGER = NON_POSITIVE_INTEGER = NEGATIVE_INTEGER = NON_NEGATIVE_INTEGER = ->(xdm_atomic_value) {
46
+ xdm_atomic_value.to_java.getValue
47
+ }
48
+ DECIMAL = ->(xdm_atomic_value) {
49
+ BigDecimal(xdm_atomic_value.to_s)
50
+ }
51
+ FLOAT = DOUBLE = ->(xdm_atomic_value) {
52
+ xdm_atomic_value.to_java.getValue
53
+ }
54
+ DATE_TIME = DATE_TIME_STAMP = ValueToRuby::DateTimeConvertor
55
+ BOOLEAN = ->(xdm_atomic_value) {
56
+ xdm_atomic_value.to_java.getValue
57
+ }
58
+ NOTATION = QNAME = ->(xdm_atomic_value) {
59
+ Saxon::QName.new(xdm_atomic_value.to_java.getQNameValue)
60
+ }
61
+ BASE64_BINARY = ->(xdm_atomic_value) {
62
+ Base64.decode64(xdm_atomic_value.to_s)
63
+ }
64
+ HEX_BINARY = ->(xdm_atomic_value) {
65
+ bytes = []
66
+ xdm_atomic_value.to_s.scan(/../) { |x| bytes << x.hex.chr(Encoding::ASCII_8BIT) }
67
+ bytes.join
68
+ }
69
+ BYTE = ->(xdm_atomic_value) {
70
+ [xdm_atomic_value.to_java.getLongValue].pack('c')
71
+ }
72
+ UNSIGNED_BYTE = ->(xdm_atomic_value) {
73
+ [xdm_atomic_value.to_java.getLongValue].pack('C')
74
+ }
75
+ end
76
+ end
77
+ end
78
+ end
data/lib/saxon/jaxp.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'java'
2
+
3
+ module Saxon
4
+ # an easy-to-access place to keep JAXP-related Java classes
5
+ module JAXP
6
+ include_package 'javax.xml.transform.stream'
7
+ end
8
+ end
@@ -0,0 +1,93 @@
1
+ require 'pathname'
2
+ require 'java'
3
+
4
+ module Saxon
5
+ # A sensible namespace to put Saxon Java classes into
6
+ module S9API
7
+ end
8
+
9
+ module Loader
10
+ LOAD_SEMAPHORE = Mutex.new
11
+
12
+ # Error raised if Saxon::Loader.load! is called but the path handed
13
+ # in does not exist or is not a directory
14
+ class NoJarsError < StandardError
15
+ def initialize(path)
16
+ @path = path
17
+ end
18
+
19
+ def to_s
20
+ "The path ('#{@path}') you supplied for the Saxon .jar files doesn't exist, sorry"
21
+ end
22
+ end
23
+
24
+ # Error raised if Saxon::Loader.load! is called but the path handed
25
+ # in does not contain the Saxon .jars
26
+ class MissingJarError < StandardError
27
+ def initialize(path)
28
+ @path = path
29
+ end
30
+
31
+ def to_s
32
+ "One of saxon9he.jar, saxon9pe.jar, or saxon9ee.jar must be present in the path ('#{@path}') you supplied, sorry"
33
+ end
34
+ end
35
+
36
+ # @param saxon_home [String, Pathname] the path to the dir containing
37
+ # Saxon's .jars. Defaults to the vendored Saxon HE
38
+ # @return [true, false] Returns true if Saxon had not been loaded and
39
+ # is now, and false if Saxon was already loaded
40
+ def self.load!(saxon_home = nil)
41
+ return false if instance_variable_defined?(:@saxon_loaded) && @saxon_loaded
42
+ LOAD_SEMAPHORE.synchronize do
43
+ if Saxon::S9API.const_defined?(:Processor)
44
+ false
45
+ else
46
+ if jars_not_on_classpath?
47
+ if saxon_home.nil?
48
+ require 'saxon_jars'
49
+ else
50
+ saxon_home = Pathname.new(saxon_home)
51
+ raise NoJarsError, saxon_home unless saxon_home.directory?
52
+ jars = [main_jar(saxon_home)].compact
53
+ raise MissingJarError if jars.empty?
54
+ jars += extra_jars(saxon_home)
55
+
56
+ add_jars_to_classpath!(saxon_home, jars)
57
+ end
58
+ end
59
+
60
+ @saxon_loaded = true
61
+ true
62
+ end
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def self.main_jar(path)
69
+ ['saxon9he.jar', 'saxon9pe.jar', 'saxon9ee.jar'].map { |jar| path.join(jar) }.find { |jar| jar.file? }
70
+ end
71
+
72
+ def self.extra_jars(path)
73
+ optional = ['saxon9-unpack.jar', 'saxon9-sql.jar'].map { |jar| path.join(jar) }.select { |jar| jar.file? }
74
+ icu = path.children.find { |jar| jar.extname == '.jar' && !jar.basename.to_s.match(/^saxon-icu|^icu4j/).nil? }
75
+ ([icu] + optional).compact
76
+ end
77
+
78
+ def self.jars_not_on_classpath?
79
+ begin
80
+ Java::net.sf.saxon.s9api.Processor
81
+ false
82
+ rescue
83
+ true
84
+ end
85
+ end
86
+
87
+ def self.add_jars_to_classpath!(saxon_home, jars)
88
+ jars.each do |jar|
89
+ $CLASSPATH << jar.to_s
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,33 @@
1
+ require_relative 's9api'
2
+
3
+ module Saxon
4
+ # Provides simple access to Saxon's OccurrenceIndicator constants,
5
+ # for use in declaring variable types
6
+ module OccurrenceIndicator
7
+ class << self
8
+ def one
9
+ @one ||= Saxon::S9API::OccurrenceIndicator::ONE
10
+ end
11
+
12
+ def one_or_more
13
+ @one_or_more ||= Saxon::S9API::OccurrenceIndicator::ONE_OR_MORE
14
+ end
15
+
16
+ def zero
17
+ @zero ||= Saxon::S9API::OccurrenceIndicator::ZERO
18
+ end
19
+
20
+ def zero_or_more
21
+ @zero_or_more ||= Saxon::S9API::OccurrenceIndicator::ZERO_OR_MORE
22
+ end
23
+
24
+ def zero_or_one
25
+ @zero_or_one ||= Saxon::S9API::OccurrenceIndicator::ZERO_OR_ONE
26
+ end
27
+
28
+ def indicator_names
29
+ @indicator_names ||= (public_methods(false) - Object.public_methods - [:indicator_names])
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,127 @@
1
+ require 'saxon/s9api'
2
+
3
+ module Saxon
4
+ # Wraps the <tt>net.sf.saxon.lib.ParseOptions</tt> class, which holds default
5
+ # options to be passed to the parser
6
+ class ParseOptions
7
+ attr_reader :parse_options
8
+ private :parse_options
9
+
10
+ # @api private
11
+ # @param parse_options [net.sf.saxon.lib.ParseOptions, nil] The Saxon
12
+ # ParseOptions instance to wrap
13
+ def initialize(parse_options = nil)
14
+ @parse_options = parse_options || Saxon::S9API::ParseOptions.new
15
+ end
16
+
17
+ # Provide hash-like access to SAX parser properties
18
+ # @return [Saxon::ParseOptions::ParserProperties] the properties object
19
+ def properties
20
+ @properties ||= ParserProperties.new(parse_options)
21
+ end
22
+
23
+ # Provide hash-like access to SAX parser features
24
+ # @return [Saxon::ParseOptions::ParserFeatures] the features object
25
+ def features
26
+ @features ||= ParserFeatures.new(parse_options)
27
+ end
28
+ end
29
+
30
+ # Provides idiomatic access to parser properties
31
+ class ParserProperties
32
+ include Enumerable
33
+
34
+ attr_reader :parse_options
35
+ private :parse_options
36
+
37
+ # @api private
38
+ # @param [Saxon::ParseOptions] parse_options The ParseOptions object this
39
+ # belongs to
40
+ def initialize(parse_options)
41
+ @parse_options ||= parse_options
42
+ end
43
+
44
+ # @return [Hash] the currently-set properties as a (frozen) hash
45
+ def to_h
46
+ (parse_options.getParserProperties || {}).freeze
47
+ end
48
+
49
+ # Fetch the current value of a Parser Property
50
+ #
51
+ # @param [String, URI] uri The Parser Property URI to fetch
52
+ # @return [Object] The current value of the property
53
+ def [](uri)
54
+ return nil if parse_options.getParserProperties.nil?
55
+ parse_options.getParserProperty(uri)
56
+ end
57
+
58
+ # Set the value of a Parser Property
59
+ #
60
+ # @param [String, URI] uri The Parser Property URI to set
61
+ # @param [Object] value The value to set the property to
62
+ # @return [Object] The new value of the property
63
+ def []=(uri, value)
64
+ parse_options.addParserProperties(uri, value)
65
+ self[uri]
66
+ end
67
+
68
+ # Iterate across each currently-set property, in the same way as a hash
69
+ #
70
+ # @yield [uri, value]
71
+ # @yieldparam uri [String] The URI of the property as a String
72
+ # @yieldparam value [Object] The value of the property
73
+ def each(&block)
74
+ to_h.each(&block)
75
+ end
76
+ end
77
+
78
+ # Provides idiomatic access to parser properties
79
+ class ParserFeatures
80
+ include Enumerable
81
+
82
+ attr_reader :parse_options
83
+ private :parse_options
84
+
85
+ # @api private
86
+ # @param [Saxon::ParseOptions] parse_options The ParseOptions object this
87
+ # belongs to
88
+ def initialize(parse_options)
89
+ @parse_options ||= parse_options
90
+ end
91
+
92
+ # @return [Hash] the currently-set features as a (frozen) hash
93
+ def to_h
94
+ (parse_options.getParserFeatures || {}).freeze
95
+ end
96
+
97
+ # Fetch the current value of a Parser Feature
98
+ #
99
+ # @param [String, URI] uri The Parser Feature URI to fetch
100
+ # @return [Boolean, nil] The current value of the feature, or nil if it's
101
+ # unset
102
+ def [](uri)
103
+ pf = parse_options.getParserFeatures
104
+ return nil if pf.nil? || !pf.include?(uri)
105
+ parse_options.getParserFeature(uri)
106
+ end
107
+
108
+ # Set the value of a Parser Feature
109
+ #
110
+ # @param [String, URI] uri The Parser Property URI to set
111
+ # @param [Boolean] value The value to set the property to
112
+ # @return [Boolean] The new value of the property
113
+ def []=(uri, value)
114
+ parse_options.addParserFeature(uri, value)
115
+ self[uri]
116
+ end
117
+
118
+ # Iterate across each currently-set feature, in the same way as a hash
119
+ #
120
+ # @yield [uri, value]
121
+ # @yieldparam uri [String] The URI of the feature as a String
122
+ # @yieldparam value [Boolean] Whether the feature is on or off
123
+ def each(&block)
124
+ to_h.each(&block)
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,102 @@
1
+ require 'saxon/s9api'
2
+ require 'saxon/source'
3
+ require 'saxon/configuration'
4
+ require 'saxon/document_builder'
5
+ require 'saxon/xpath'
6
+ require 'saxon/xslt'
7
+
8
+ module Saxon
9
+ # Saxon::Processor wraps the S9API::Processor object. This is the object
10
+ # responsible for creating an XSLT compiler or an XML Document object.
11
+ #
12
+ # The Processor is threadsafe, and can be shared between threads. But, most
13
+ # importantly XSLT or XML objects created by a Processor can only be used
14
+ # with other XSLT or XML objects created by the same Processor instance.
15
+ class Processor
16
+ # Provides a processor with default configuration. Essentially a singleton
17
+ # instance
18
+ # @return [Saxon::Processor]
19
+ def self.default
20
+ @processor ||= create(Saxon::Configuration.default)
21
+ end
22
+
23
+ # @param config [File, String, IO, Saxon::Configuration] an open File, or string,
24
+ # containing a Saxon configuration file; an existing Saxon::Configuration
25
+ # object
26
+ # @return [Saxon::Processor]
27
+ def self.create(config = nil)
28
+ Saxon::Loader.load!
29
+ case config
30
+ when nil
31
+ licensed_or_config_source = false
32
+ when Saxon::Configuration
33
+ licensed_or_config_source = config.to_java
34
+ else
35
+ licensed_or_config_source = Saxon::Source.create(config).to_java
36
+ end
37
+ s9_processor = S9API::Processor.new(licensed_or_config_source)
38
+ new(s9_processor)
39
+ end
40
+
41
+ # @api private
42
+ # @param [net.sf.saxon.s9api.Processor] s9_processor The Saxon Processor
43
+ # instance to wrap
44
+ def initialize(s9_processor)
45
+ @s9_processor = s9_processor
46
+ end
47
+
48
+ # Generate a new DocumentBuilder that uses this Processor.
49
+ # Sharing DocumentBuilders across threads is not recommended/
50
+ #
51
+ # @return [Saxon::DocumentBuilder] A new Saxon::DocumentBuilder
52
+ def document_builder
53
+ Saxon::DocumentBuilder.new(@s9_processor.newDocumentBuilder)
54
+ end
55
+
56
+ # Declare custom collations for use by XSLT, XPath, and XQuery processors
57
+ #
58
+ # @param collations [Hash<String => java.text.Collator>] collations to
59
+ # declare, as a hash of URI => Collator
60
+ def declare_collations(collations)
61
+ collations.each do |uri, collation|
62
+ @s9_processor.declareCollation(uri, collation)
63
+ end
64
+ end
65
+ # Generate a new <tt>XPath::Compiler</tt> that uses this
66
+ # <tt>Processor</tt>. Sharing <tt>XPath::Compiler</tt>s across threads is
67
+ # fine as long as the static context is not changed.
68
+ #
69
+ # @yield An XPath compiler DSL block, see {Saxon::XPath::Compiler.create}
70
+ # @return [Saxon::XPath::Compiler] a new XPath compiler
71
+ def xpath_compiler(&block)
72
+ Saxon::XPath::Compiler.create(self, &block)
73
+ end
74
+
75
+ # Generate a new <tt>XSLT::Compiler</tt> that uses this
76
+ # <tt>Processor</tt>. Sharing <tt>XSLT::Compiler</tt>s across threads is
77
+ # fine as long as the static context is not changed.
78
+ #
79
+ # @yield An XPath compiler DSL block, see {Saxon::XSLT::Compiler.create}
80
+ # @return [Saxon::XSLT::Compiler] a new XSLT compiler
81
+ def xslt_compiler(&block)
82
+ Saxon::XSLT::Compiler.create(self, &block)
83
+ end
84
+
85
+ # @return [net.sf.saxon.s9api.Processor] The underlying Saxon processor
86
+ def to_java
87
+ @s9_processor
88
+ end
89
+
90
+ # compare equal if the underlying java processor is the same instance for
91
+ # self and other
92
+ # @param other object to compare against
93
+ def ==(other)
94
+ other.to_java === to_java
95
+ end
96
+
97
+ # @return [Saxon::Configuration] This processor's configuration instance
98
+ def config
99
+ @config ||= Saxon::Configuration.create(self)
100
+ end
101
+ end
102
+ end