saxon-rb 0.4.0-java → 0.7.2-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +429 -42
  3. data/.ruby-version +1 -1
  4. data/.yardopts +1 -0
  5. data/Gemfile +2 -2
  6. data/README.md +358 -10
  7. data/Rakefile +237 -7
  8. data/docs/templates/plugin.rb +73 -0
  9. data/lib/net/sf/saxon/Saxon-HE/{9.9.1-5/Saxon-HE-9.9.1-5.jar → 9.9.1-6/Saxon-HE-9.9.1-6.jar} +0 -0
  10. data/lib/saxon-rb.rb +0 -0
  11. data/lib/{saxon_jars.rb → saxon-rb_jars.rb} +2 -2
  12. data/lib/saxon.rb +13 -0
  13. data/lib/saxon/axis_iterator.rb +8 -1
  14. data/lib/saxon/configuration.rb +16 -13
  15. data/lib/saxon/document_builder.rb +216 -5
  16. data/lib/saxon/feature_flags.rb +11 -0
  17. data/lib/saxon/feature_flags/errors.rb +8 -0
  18. data/lib/saxon/feature_flags/helpers.rb +15 -0
  19. data/lib/saxon/feature_flags/version.rb +100 -0
  20. data/lib/saxon/item_type.rb +129 -89
  21. data/lib/saxon/item_type/lexical_string_conversion.rb +214 -59
  22. data/lib/saxon/item_type/value_to_ruby.rb +25 -0
  23. data/lib/saxon/loader.rb +6 -1
  24. data/lib/saxon/nokogiri.rb +78 -0
  25. data/lib/saxon/occurrence_indicator.rb +32 -3
  26. data/lib/saxon/processor.rb +50 -5
  27. data/lib/saxon/qname.rb +37 -2
  28. data/lib/saxon/s9api.rb +5 -0
  29. data/lib/saxon/sequence_type.rb +131 -0
  30. data/lib/saxon/serializer.rb +3 -137
  31. data/lib/saxon/serializer/destination.rb +80 -0
  32. data/lib/saxon/serializer/object.rb +93 -0
  33. data/lib/saxon/serializer/output_properties.rb +83 -0
  34. data/lib/saxon/source.rb +207 -71
  35. data/lib/saxon/version.rb +7 -1
  36. data/lib/saxon/version/library.rb +89 -0
  37. data/lib/saxon/xdm.rb +7 -0
  38. data/lib/saxon/xdm/array.rb +16 -0
  39. data/lib/saxon/xdm/atomic_value.rb +10 -2
  40. data/lib/saxon/xdm/empty_sequence.rb +13 -0
  41. data/lib/saxon/xdm/external_object.rb +1 -0
  42. data/lib/saxon/xdm/function_item.rb +1 -0
  43. data/lib/saxon/xdm/item.rb +7 -0
  44. data/lib/saxon/xdm/map.rb +38 -0
  45. data/lib/saxon/xdm/node.rb +50 -1
  46. data/lib/saxon/xdm/sequence_like.rb +15 -0
  47. data/lib/saxon/xdm/value.rb +21 -5
  48. data/lib/saxon/xpath.rb +9 -0
  49. data/lib/saxon/xpath/compiler.rb +37 -2
  50. data/lib/saxon/xpath/executable.rb +53 -28
  51. data/lib/saxon/xpath/static_context.rb +25 -40
  52. data/lib/saxon/xpath/variable_declaration.rb +16 -49
  53. data/lib/saxon/xslt.rb +12 -0
  54. data/lib/saxon/xslt/compiler.rb +75 -6
  55. data/lib/saxon/xslt/evaluation_context.rb +30 -4
  56. data/lib/saxon/xslt/executable.rb +206 -29
  57. data/lib/saxon/xslt/invocation.rb +97 -0
  58. data/saxon-rb.gemspec +3 -3
  59. metadata +22 -10
  60. data/saxon.gemspec +0 -30
@@ -9,24 +9,31 @@ require_relative 'xdm/empty_sequence'
9
9
  require_relative 'xdm/item'
10
10
 
11
11
  module Saxon
12
+ # Classes for representing, creating, and working with the XPath Data Model
13
+ # type system used in XPath 2+, XSLT 2+, and XQuery.
12
14
  module XDM
13
15
  class << self
16
+ # Convenience function for creating a new {AtomicValue}. See {AtomicValue.create}
14
17
  def AtomicValue(*args)
15
18
  XDM::AtomicValue.create(*args)
16
19
  end
17
20
 
21
+ # Convenience function for creating a new {Value}. See {Value.create}
18
22
  def Value(*args)
19
23
  XDM::Value.create(*args)
20
24
  end
21
25
 
26
+ # Returns the XDM {EmptySequence}. See {EmptySequence.create}
22
27
  def EmptySequence()
23
28
  XDM::EmptySequence.create
24
29
  end
25
30
 
31
+ # Convenience function for creating a new {Array}. See {Array.create}
26
32
  def Array(*args)
27
33
  XDM::Array.create(*args)
28
34
  end
29
35
 
36
+ # Convenience function for creating a new {Map}. See {Map.create}
30
37
  def Map(*args)
31
38
  XDM::Map.create(*args)
32
39
  end
@@ -5,6 +5,11 @@ module Saxon
5
5
  module XDM
6
6
  # Represents an XDM Array
7
7
  class Array
8
+ # Create a new {XDM::Array} from a Ruby Array. The contents of the array
9
+ # will be converted to {XDM::Value}s using {XDM.Value()}. An existing
10
+ # {S9API::XdmArray} will simply be wrapped and returned.
11
+ #
12
+ # @return [XDM::Array] the new XDM Array
8
13
  def self.create(array)
9
14
  case array
10
15
  when S9API::XdmArray
@@ -28,14 +33,19 @@ module Saxon
28
33
  @s9_xdm_array = s9_xdm_array
29
34
  end
30
35
 
36
+ # Iterate over the Array, yielding each element.
37
+ # @yieldparam value [XDM::Value] the current value from the Array
31
38
  def each(&block)
32
39
  cached_array.each(&block)
33
40
  end
34
41
 
42
+ # Fetch element at index +i+ in the array.
43
+ # @param i [Integer] the index of the element to retrieve.
35
44
  def [](i)
36
45
  cached_array[i]
37
46
  end
38
47
 
48
+ # @return [Integer] the length of the array
39
49
  def length
40
50
  s9_xdm_array.arrayLength
41
51
  end
@@ -53,16 +63,22 @@ module Saxon
53
63
  cached_array == other.to_a
54
64
  end
55
65
 
66
+ # Return a (frozen) Ruby {::Array} containing all the elements of the {XDM::Array}
56
67
  def to_a
57
68
  cached_array
58
69
  end
59
70
 
60
71
  alias_method :eql?, :==
61
72
 
73
+ # Compute a hash-code for this {Array}.
74
+ #
75
+ # Two {XDM::Array}s with the same content will have the same hash code (and will compare using eql?).
76
+ # @see Object#hash
62
77
  def hash
63
78
  @hash ||= cached_array.hash
64
79
  end
65
80
 
81
+ # @return the underlying Java XdmArray
66
82
  def to_java
67
83
  s9_xdm_array
68
84
  end
@@ -20,6 +20,7 @@ module Saxon
20
20
  # them imlpicitly through the XDM::AtomicValue creation process doesn't really
21
21
  # work. They need to be created explicitly and then handed in to be wrapped.
22
22
  class CannotCreateQNameFromString < StandardError
23
+ # returns an error message
23
24
  def to_s
24
25
  "QName XDM::AtomicValues must be created using an instance of Saxon::QName, not a string like 'prefix:name': Prefix URI binding is undefined at this point"
25
26
  end
@@ -29,19 +30,22 @@ module Saxon
29
30
  # isn't a way to create these outside of parsing an XML document within
30
31
  # Saxon, so attempting to do so raises this error.
31
32
  class NotationCannotBeDirectlyCreated < StandardError
32
- def to_s
33
+ # returns an error message
34
+ def to_s
33
35
  "xs:NOTATION XDM::AtomicValues cannot be directly created outside of XML parsing."
34
36
  end
35
37
  end
36
38
 
39
+ # ItemType representing QNames
37
40
  XS_QNAME = ItemType.get_type('xs:QName')
41
+ # ItemType representing NOTATION
38
42
  XS_NOTATION = ItemType.get_type('xs:NOTATION')
39
43
 
40
44
  class << self
41
45
  # Convert a single Ruby value into an XDM::AtomicValue
42
46
  #
43
47
  # If no explicit {ItemType} is passed, the correct type is guessed based
44
- # on the class of the value. (e.g. <tt>xs:date</tt> for {Date}.)
48
+ # on the class of the value. (e.g. <tt>xs:date</tt> for {::Date}.)
45
49
  #
46
50
  # Values are converted based on Ruby idioms and operations, so an explicit
47
51
  # {ItemType} of <tt>xs:boolean</tt> will use truthyness to evaluate the
@@ -165,6 +169,10 @@ module Saxon
165
169
 
166
170
  alias_method :eql?, :==
167
171
 
172
+ # Compute a hash-code for this {AtomicValue}.
173
+ #
174
+ # Two {AtomicValue}s with the same content will have the same hash code (and will compare using eql?).
175
+ # @see Object#hash
168
176
  def hash
169
177
  @hash ||= s9_xdm_atomic_value.hashCode
170
178
  end
@@ -5,30 +5,43 @@ module Saxon
5
5
  module XDM
6
6
  # Represents the empty sequence in XDM
7
7
  class EmptySequence
8
+ # Returns an instance. Effectively a Singleton because the EmptySequence
9
+ # is immutable, and empty. An instance is completely interchangeable with
10
+ # another. The instance is cached, but multiple instances may exist across
11
+ # threads. We don't prevent that because it's immaterial.
12
+ # @return [EmptySequence] The empty sequence
8
13
  def self.create
9
14
  @instance ||= new
10
15
  end
11
16
 
12
17
  include SequenceLike
13
18
 
19
+ # @return [Enumerator] an enumerator over an empty Array
14
20
  def sequence_enum
15
21
  [].to_enum
16
22
  end
17
23
 
24
+ # @return [Integer] the size of the sequence (always 0)
18
25
  def sequence_size
19
26
  0
20
27
  end
21
28
 
29
+ # All instances of {EmptySequence} are equal to each other.
30
+ #
31
+ # @param other [Object] the object to compare self against
32
+ # @return [Boolean] Whether this object is equal to the other
22
33
  def ==(other)
23
34
  other.class == self.class
24
35
  end
25
36
 
26
37
  alias_method :eql?, :==
27
38
 
39
+ # @return [Integer] the hash code. All instances have the same hash code.
28
40
  def hash
29
41
  [].hash
30
42
  end
31
43
 
44
+ # @return [net.sf.saxon.s9api.XDMEmptySequence] the underlying Java empty sequence
32
45
  def to_java
33
46
  @s9_xdm_empty_sequence ||= Saxon::S9API::XdmEmptySequence.getInstance
34
47
  end
@@ -13,6 +13,7 @@ module Saxon
13
13
  @s9_xdm_external_object = s9_xdm_external_object
14
14
  end
15
15
 
16
+ # The underlying Saxon XdmExternalObject
16
17
  def to_java
17
18
  @s9_xdm_external_object
18
19
  end
@@ -13,6 +13,7 @@ module Saxon
13
13
  @s9_xdm_function_item = s9_xdm_function_item
14
14
  end
15
15
 
16
+ # The underlying Saxon XdmFunctionItem
16
17
  def to_java
17
18
  @s9_xdm_function_item
18
19
  end
@@ -1,5 +1,12 @@
1
1
  module Saxon
2
2
  module XDM
3
+ # Create one of the XdmItem-derived XDM objects from the passed in argument.
4
+ #
5
+ # Existing XDM::* objects are passed through. s9api.Xdm* Java objects are
6
+ # wrapped appropriately and returned. Ruby Arrays and Hashes are converted
7
+ # to {XDM::Array} and {XDM::Map} instances respectively. Ruby values that
8
+ # respond to +#each+ are converted to an {XDM::Array} (e.g. {Set}). Other
9
+ # Ruby values are converted to {XDM::AtomicValue}.
3
10
  def self.Item(item)
4
11
  case item
5
12
  when Value, AtomicValue, Node, Array, Map, ExternalObject
@@ -5,6 +5,12 @@ module Saxon
5
5
  module XDM
6
6
  # Represents an XDM Map
7
7
  class Map
8
+ # Create an {XDM::Map} from a Ruby Hash, by ensuring each key has been
9
+ # converted to an {AtomicValue}, and each value has been converted to an
10
+ # XDM Value of some sort.
11
+ # @return [XDM::Map] the new Map
12
+ # @see XDM.AtomicValue
13
+ # @see XDM.Value
8
14
  def self.create(hash)
9
15
  case hash
10
16
  when Saxon::S9API::XdmMap
@@ -30,39 +36,71 @@ module Saxon
30
36
  @s9_xdm_map = s9_xdm_map
31
37
  end
32
38
 
39
+ # Compare this Map against another. They're equal if they contain the same
40
+ # key, value pairs.
33
41
  def ==(other)
34
42
  return false unless other.is_a?(self.class)
35
43
  to_h == other.to_h
36
44
  end
37
45
 
46
+ # Fetch the value for the key given. +key+ is converted to an
47
+ # {XDM::AtomicValue} if it isn't already one.
48
+ # @param key [Object, XDM::AtomicValue] the key to retrieve
38
49
  def [](key)
39
50
  cached_hash[XDM.AtomicValue(key)]
40
51
  end
41
52
 
53
+ # Fetch the value for the key given, as {Hash#fetch} would. +key+ is
54
+ # converted to an {XDM::AtomicValue} if it isn't already one.
55
+ # @param key [XDM::AtomicValue, Object] the key to retrieve.
56
+ # @see Hash#fetch
42
57
  def fetch(key, *args, &block)
43
58
  cached_hash.fetch(XDM.AtomicValue(key), *args, &block)
44
59
  end
45
60
 
61
+ # Iterate over the Map as {Hash#each} would
62
+ # @yieldparam key [XDM::AtomicValue] the key
63
+ # @yieldparam value [XDM::Value] the value
46
64
  def each(&block)
47
65
  cached_hash.each(&block)
48
66
  end
49
67
 
68
+ # Return a new Map containing only key, value pairs for which the block
69
+ # returns true.
70
+ # @yieldparam key [XDM::AtomicValue] the key
71
+ # @yieldparam value [XDM::Value] the value
72
+ # @see ::Hash#select
50
73
  def select(&block)
51
74
  self.class.create(each.select(&block).to_h)
52
75
  end
53
76
 
77
+ # Return a new Map containing only key, value pairs for which the block
78
+ # DOES NOT return true.
79
+ # @yieldparam key [XDM::AtomicValue] the key
80
+ # @yieldparam value [XDM::Value] the value
81
+ # @see ::Hash#reject
54
82
  def reject(&block)
55
83
  self.class.create(each.reject(&block).to_h)
56
84
  end
57
85
 
86
+ # Create a new Map from the result of merging another Map into this one.
87
+ # In the case of duplicate keys, the value in the provided hash will be
88
+ # used.
89
+ # @yieldparam key [XDM::AtomicValue] the key
90
+ # @yieldparam value [XDM::Value] the value
91
+ # @return [XDM::Map] the new Map
92
+ # @see ::Hash#merge
58
93
  def merge(other)
59
94
  self.class.create(to_h.merge(other.to_h))
60
95
  end
61
96
 
97
+ # @return [S9API::XdmMap] the underlying Saxon XdmMap
62
98
  def to_java
63
99
  @s9_xdm_map
64
100
  end
65
101
 
102
+ # a (frozen) Ruby hash containing the keys and values from the Map.
103
+ # @return [Hash] the Map as a Ruby hash.
66
104
  def to_h
67
105
  cached_hash
68
106
  end
@@ -4,7 +4,8 @@ require_relative 'sequence_like'
4
4
 
5
5
  module Saxon
6
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.
7
+ # An XPath Data Model Node object, representing an XML document, or an
8
+ # element or one of the other node chunks in the XDM.
8
9
  class Node
9
10
  include XDM::SequenceLike
10
11
  include XDM::ItemSequenceLike
@@ -23,12 +24,20 @@ module Saxon
23
24
  @s9_xdm_node
24
25
  end
25
26
 
27
+ # The name of the node, as a {Saxon::QName}, or +nil+ if the node is not
28
+ # of a kind that has a name
29
+ # @return [Saxon::QName, null] the name, if there is one
26
30
  def node_name
27
31
  return @node_name if instance_variable_defined?(:@node_name)
28
32
  node_name = s9_xdm_node.getNodeName
29
33
  @node_name = node_name.nil? ? nil : Saxon::QName.new(node_name)
30
34
  end
31
35
 
36
+ # What kind of node this is. Returns one of +:document+, +:element+,
37
+ # +:text+, +:attribute+, +:namespace+, +:comment+,
38
+ # +:processing_instruction+, or +:comment+
39
+ #
40
+ # @return [Symbol] the kind of node this is
32
41
  def node_kind
33
42
  @node_kind ||= case s9_xdm_node.nodeKind
34
43
  when Saxon::S9API::XdmNodeKind::ELEMENT
@@ -48,6 +57,7 @@ module Saxon
48
57
  end
49
58
  end
50
59
 
60
+ # Does this Node represent the same underlying node as the other?
51
61
  def ==(other)
52
62
  return false unless other.is_a?(XDM::Node)
53
63
  s9_xdm_node.equals(other.to_java)
@@ -55,17 +65,56 @@ module Saxon
55
65
 
56
66
  alias_method :eql?, :==
57
67
 
68
+ # Compute a hash-code for this {Node}.
69
+ #
70
+ # Two {Node}s with the same content will have the same hash code (and will compare using eql?).
71
+ # @see Object#hash
58
72
  def hash
59
73
  @hash ||= s9_xdm_node.hashCode
60
74
  end
61
75
 
76
+ # Execute the given block for every child node of this
77
+ # @yieldparam node [Saxon::XDM::Node] the child node
62
78
  def each(&block)
63
79
  axis_iterator(:child).each(&block)
64
80
  end
65
81
 
82
+ # Create an {AxisIterator} over this Node for the given XPath axis
83
+ # @param axis [Symbol] the axis to iterate along
84
+ # @see AxisIterator
66
85
  def axis_iterator(axis)
67
86
  AxisIterator.new(self, axis)
68
87
  end
88
+
89
+ # Use Saxon's naive XDM Node serialisation to serialize the node and its
90
+ # descendants. Saxon uses a new Serializer with default options to
91
+ # serialize the node. In particular, if the Node was produced by an XSLT
92
+ # that used +<xsl:character-map>+ through +<xsl:output>+ to modify the
93
+ # contents, then they *WILL* *NOT* have been applied.
94
+ #
95
+ # +<xsl:output>+ has its effect at serialization time, not at XDM tree
96
+ # creation time, so it won't be applied until you serialize the document.
97
+ #
98
+ # Even then, unless you use a {Serializer} configured with the XSLT's
99
+ # +<xsl:output>+. We make a properly configured serializer available in
100
+ # the result of any XSLT transform (a {Saxon::XSLT::Invocation}), e.g.:
101
+ #
102
+ # result = xslt.apply_templates(input)
103
+ # result.to_s
104
+ # # or
105
+ # result.serialize('/path/to/output.xml')
106
+ #
107
+ # You can also get that {Serializer} directly from {XSLT::Executable},
108
+ # should you want to use the serialization options from a particular XSLT
109
+ # to serialize arbitrary XDM values:
110
+ #
111
+ # serializer = xslt.serializer
112
+ # serializer.serialize(an_xdm_value)
113
+ #
114
+ # @see http://www.saxonica.com/documentation9.9/index.html#!javadoc/net.sf.saxon.s9api/XdmNode@toString
115
+ def to_s
116
+ s9_xdm_node.toString
117
+ end
69
118
  end
70
119
  end
71
120
  end
@@ -1,14 +1,25 @@
1
1
  module Saxon
2
2
  module XDM
3
+ # Mixin for objects that are XDM Sequence-like in behaviour
3
4
  module SequenceLike
5
+ # Implementors should return an {Enumerator} over the Sequence. For
6
+ # {XDM::Value}s, this will just be the items in the sequence. For
7
+ # XDM::AtomicValue or XDM::Node, this will be a single-item Enumerator so
8
+ # that Items can be correctly treated as single-item Values.
4
9
  def sequence_enum
5
10
  raise NotImplementedError
6
11
  end
7
12
 
13
+ # Implementors should return the size of the Sequence. For
14
+ # {XDM::AtomicValue} this will always be 1.
15
+ # @return [Integer] the sequence size
8
16
  def sequence_size
9
17
  raise NotImplementedError
10
18
  end
11
19
 
20
+ # Return a new XDM::Value from this Sequence with the passed in value
21
+ # appended to the end.
22
+ # @return [XDM::Value] the new Value
12
23
  def append(other)
13
24
  XDM::Value.create([self, other])
14
25
  end
@@ -17,11 +28,15 @@ module Saxon
17
28
  alias_method :+, :append
18
29
  end
19
30
 
31
+ # Mixin for objects that are Sequence-like but only contain a single item,
32
+ # like {XDM::AtomicValue} or {XDM::Node}
20
33
  module ItemSequenceLike
34
+ # return a single-item Enumerator containing +self+
21
35
  def sequence_enum
22
36
  [self].to_enum
23
37
  end
24
38
 
39
+ # Returns the sequence size, which will always be 1.
25
40
  def sequence_size
26
41
  1
27
42
  end
@@ -19,8 +19,8 @@ module Saxon
19
19
  when 0
20
20
  XDM.EmptySequence()
21
21
  when 1
22
- if existing_value = maybe_xdm_value(items.first)
23
- return existing_value
22
+ if value = maybe_xdm_value(items.first)
23
+ return value
24
24
  end
25
25
  XDM.Item(items.first)
26
26
  else
@@ -30,12 +30,25 @@ module Saxon
30
30
 
31
31
  private
32
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)
33
+ def maybe_xdm_value(value)
34
+ return value if value.is_a?(self)
35
+ return value if value === XDM.EmptySequence()
36
+ return XDM.EmptySequence() if value.instance_of?(Saxon::S9API::XdmEmptySequence)
37
+ return check_for_empty_or_single_item_value(value) if value.instance_of?(Saxon::S9API::XdmValue)
36
38
  false
37
39
  end
38
40
 
41
+ def check_for_empty_or_single_item_value(s9_value)
42
+ case s9_value.size
43
+ when 0
44
+ XDM.EmptySequence()
45
+ when 1
46
+ XDM.Item(s9_value.item_at(0))
47
+ else
48
+ new(s9_value)
49
+ end
50
+ end
51
+
39
52
  def wrap_items(items)
40
53
  result = []
41
54
  items.map { |item|
@@ -122,10 +135,12 @@ module Saxon
122
135
 
123
136
  alias_method :enum_for, :to_enum
124
137
 
138
+ # Returns an enumerator over the Sequence
125
139
  def sequence_enum
126
140
  to_enum
127
141
  end
128
142
 
143
+ # @return [Integer] the size of the sequence
129
144
  def sequence_size
130
145
  s9_xdm_value.size
131
146
  end
@@ -137,6 +152,7 @@ module Saxon
137
152
  @s9_xdm_item = s9_xdm_item
138
153
  end
139
154
 
155
+ # Return the underlying s9api XdmItem
140
156
  def to_java
141
157
  @s9_xdm_item
142
158
  end