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,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,103 @@
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 @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
+ import_classes_to_namespace!
60
+
61
+ @saxon_loaded = true
62
+ true
63
+ end
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def self.main_jar(path)
70
+ ['saxon9he.jar', 'saxon9pe.jar', 'saxon9ee.jar'].map { |jar| path.join(jar) }.find { |jar| jar.file? }
71
+ end
72
+
73
+ def self.extra_jars(path)
74
+ optional = ['saxon9-unpack.jar', 'saxon9-sql.jar'].map { |jar| path.join(jar) }.select { |jar| jar.file? }
75
+ icu = path.children.find { |jar| jar.extname == '.jar' && !jar.basename.to_s.match(/^saxon-icu|^icu4j/).nil? }
76
+ ([icu] + optional).compact
77
+ end
78
+
79
+ def self.jars_not_on_classpath?
80
+ begin
81
+ Java::net.sf.saxon.s9api.Processor
82
+ false
83
+ rescue
84
+ true
85
+ end
86
+ end
87
+
88
+ def self.add_jars_to_classpath!(saxon_home, jars)
89
+ jars.each do |jar|
90
+ $CLASSPATH << jar.to_s
91
+ end
92
+ end
93
+
94
+ def self.import_classes_to_namespace!
95
+ Saxon::S9API.class_eval do
96
+ include_package 'net.sf.saxon.s9api'
97
+ java_import 'net.sf.saxon.Configuration'
98
+ java_import 'net.sf.saxon.lib.FeatureKeys'
99
+ java_import 'net.sf.saxon.lib.ParseOptions'
100
+ end
101
+ end
102
+ end
103
+ 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
@@ -0,0 +1,97 @@
1
+ require 'saxon/s9api'
2
+
3
+ module Saxon
4
+ # Represents QNames
5
+ class QName
6
+ def self.clark(clark_string)
7
+ s9_qname = Saxon::S9API::QName.fromClarkName(clark_string)
8
+ new(s9_qname)
9
+ end
10
+
11
+ def self.eqname(eqname_string)
12
+ s9_qname = Saxon::S9API::QName.fromEQName(eqname_string)
13
+ new(s9_qname)
14
+ end
15
+
16
+ def self.create(opts = {})
17
+ prefix = opts[:prefix]
18
+ uri = opts[:uri]
19
+ begin
20
+ local_name = opts.fetch(:local_name)
21
+ rescue KeyError
22
+ raise ArgumentError, "The :local_name option must be passed"
23
+ end
24
+
25
+ s9_qname = Saxon::S9API::QName.new(*[prefix, uri, local_name].compact)
26
+ new(s9_qname)
27
+ end
28
+
29
+ attr_reader :s9_qname
30
+ private :s9_qname
31
+
32
+ # @api private
33
+ def initialize(s9_qname)
34
+ @s9_qname = s9_qname
35
+ end
36
+
37
+ # @return [String] The local name part of the QName
38
+ def local_name
39
+ @s9_qname.getLocalName
40
+ end
41
+
42
+ # @return [String] The prefix part of the QName ('' if unset)
43
+ def prefix
44
+ @s9_qname.getPrefix
45
+ end
46
+
47
+ # @return [String] The namespace URI part of the QName ('' if unset)
48
+ def uri
49
+ @s9_qname.getNamespaceURI
50
+ end
51
+
52
+ # Return a Clark notation representation of the QName:
53
+ #
54
+ # "{http://ns.url}local-name"
55
+ #
56
+ # Note that the prefix is lost in Clark notation.
57
+ # @return [String] The QName represented using Clark notation.
58
+ def clark
59
+ @s9_qname.getClarkName
60
+ end
61
+
62
+ # Return a Extended QName notation representation of the QName:
63
+ #
64
+ # "Q{http://ns.url}local-name"
65
+ #
66
+ # Note that the prefix is lost in EQName notation.
67
+ # @return [String] The QName represented using EQName notation.
68
+ def eqname
69
+ @s9_qname.getEQName
70
+ end
71
+
72
+ def ==(other)
73
+ return false unless other.is_a?(QName)
74
+ s9_qname.equals(other.to_java)
75
+ end
76
+ alias_method :eql?, :==
77
+
78
+ def hash
79
+ @hash ||= (local_name + uri).hash
80
+ end
81
+
82
+ def to_java
83
+ s9_qname
84
+ end
85
+
86
+ def to_s
87
+ s9_qname.to_s
88
+ end
89
+
90
+ def inspect
91
+ "<Saxon::QName @prefix=#{prefix} @uri=#{uri} @local_name=#{local_name}>"
92
+ end
93
+
94
+ class PrefixedStringWithoutNSURIError < RuntimeError
95
+ end
96
+ end
97
+ end