saxon 0.1.0-java

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