jruby-existdb 0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/conf.xml +812 -0
- data/lib/existdb.rb +74 -0
- data/lib/existdb/classwrap.rb +70 -0
- data/lib/existdb/collection.rb +98 -0
- data/lib/existdb/dom/active_record.rb +82 -0
- data/lib/existdb/dom/mapper.rb +285 -0
- data/lib/existdb/embedded.rb +84 -0
- data/lib/existdb/index_factory.rb +120 -0
- data/lib/existdb/meta.rb +41 -0
- data/lib/existdb/resource/base.rb +84 -0
- data/lib/existdb/resource/binary.rb +21 -0
- data/lib/existdb/resource/xml.rb +23 -0
- data/lib/existdb/resource_set.rb +34 -0
- data/lib/existdb/xql_factory.rb +191 -0
- data/lib/existdb/xquery_service.rb +11 -0
- data/lib/jars/antlr-2.7.7.jar +0 -0
- data/lib/jars/commons-collections-3.2.1.jar +0 -0
- data/lib/jars/commons-logging-1.1.1.jar +0 -0
- data/lib/jars/commons-pool-1.5.1.jar +0 -0
- data/lib/jars/endorsed/resolver-1.2.jar +0 -0
- data/lib/jars/endorsed/serializer-2.9.1.jar +0 -0
- data/lib/jars/endorsed/xalan-2.7.1.jar +0 -0
- data/lib/jars/endorsed/xercesImpl-2.9.1.jar +0 -0
- data/lib/jars/endorsed/xml-apis-1.3.04.jar +0 -0
- data/lib/jars/exist-lucene-module.jar +0 -0
- data/lib/jars/exist-modules.jar +0 -0
- data/lib/jars/exist.jar +0 -0
- data/lib/jars/extensions/exist-lucene-module.jar +0 -0
- data/lib/jars/extensions/exist-ngram-module.jar +0 -0
- data/lib/jars/extensions/exist-versioning.jar +0 -0
- data/lib/jars/extensions/lucene-core-2.4.1.jar +0 -0
- data/lib/jars/extensions/lucene-regex-2.4.1.jar +0 -0
- data/lib/jars/jgroups-all-2.2.6.jar +0 -0
- data/lib/jars/jta-1.1.jar +0 -0
- data/lib/jars/log4j-1.2.15.jar +0 -0
- data/lib/jars/lucene-core-2.4.1.jar +0 -0
- data/lib/jars/lucene-regex-2.4.1.jar +0 -0
- data/lib/jars/quartz-1.6.5.jar +0 -0
- data/lib/jars/sunxacml-1.2.jar +0 -0
- data/lib/jars/xmldb.jar +0 -0
- data/lib/jars/xmlrpc-client-3.1.2.jar +0 -0
- data/lib/jars/xmlrpc-common-3.1.2.jar +0 -0
- data/lib/jars/xmlrpc-server-3.1.2.jar +0 -0
- data/log4j.xml +190 -0
- metadata +104 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
module ExistDB
|
2
|
+
class Embedded
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
attr_accessor :username, :password, :url, :properties
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@username ||= 'admin'
|
9
|
+
@password ||= ''
|
10
|
+
@properties ||= {'create-database' => 'true'}
|
11
|
+
@url ||= "exist:///db"
|
12
|
+
|
13
|
+
at_exit do
|
14
|
+
stop if running?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def start
|
19
|
+
return false if running?
|
20
|
+
@impl = org.exist.xmldb.DatabaseImpl.new
|
21
|
+
@properties.each{ |key, value| @impl.setProperty(key.to_s, value.to_s) }
|
22
|
+
org.xmldb.api.DatabaseManager.registerDatabase(@impl)
|
23
|
+
@base_collection = @impl.getCollection(@url, @username, @password)
|
24
|
+
@database_instance_manager = @base_collection.getService('DatabaseInstanceManager', '1.0')
|
25
|
+
@collection_manager = @base_collection.getService('CollectionManager', '1.0')
|
26
|
+
|
27
|
+
@running = true
|
28
|
+
end
|
29
|
+
|
30
|
+
def running?
|
31
|
+
@running
|
32
|
+
end
|
33
|
+
|
34
|
+
alias :started? :running?
|
35
|
+
|
36
|
+
def stop
|
37
|
+
return false if stopped?
|
38
|
+
@database_instance_manager.shutdown
|
39
|
+
@running = false
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
def stopped?
|
44
|
+
not running?
|
45
|
+
end
|
46
|
+
|
47
|
+
def db
|
48
|
+
raise InstanceNotRunning if stopped?
|
49
|
+
ClassWrap[ @base_collection ]
|
50
|
+
end
|
51
|
+
|
52
|
+
def inspect
|
53
|
+
"#<#{self.class}:#{self.hash.to_s(16)} #{running? ? 'running' : 'stopped'}>"
|
54
|
+
end
|
55
|
+
|
56
|
+
def xquery_service
|
57
|
+
raise InstanceNotRunning if stopped?
|
58
|
+
ClassWrap[ @base_collection.getService('XQueryService', '1.0') ]
|
59
|
+
end
|
60
|
+
|
61
|
+
# org.exist.storage.BrokerPool
|
62
|
+
def broker_pool
|
63
|
+
raise InstanceNotRunning if stopped?
|
64
|
+
org.exist.storage.BrokerPool.getInstance(@impl.getName)
|
65
|
+
end
|
66
|
+
|
67
|
+
# org.exist.storage.txn.Txn
|
68
|
+
def transaction # :yields: transaction
|
69
|
+
raise InstanceNotRunning if stopped?
|
70
|
+
mgr = broker_pool.getTransactionManager
|
71
|
+
txn = mgr.beginTransaction
|
72
|
+
begin
|
73
|
+
yield txn
|
74
|
+
mgr.commit(txn)
|
75
|
+
rescue
|
76
|
+
mgr.abort(txn)
|
77
|
+
raise
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class InstanceNotRunning < Exception; end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module ExistDB
|
2
|
+
module IndexFactory
|
3
|
+
class << self
|
4
|
+
def configure(*opts, &block)
|
5
|
+
cfg = IndexFactory.new(*opts, &block)
|
6
|
+
cfg.configure
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# Example:
|
11
|
+
# ExistDB::IndexFactory.configure do
|
12
|
+
# collection '/db/PartList'
|
13
|
+
# range 'qtyOnHand' => Fixnum, 'qtyOnOrder' => Fixnum
|
14
|
+
# lucene '//part', 'name', 'description'
|
15
|
+
# end
|
16
|
+
|
17
|
+
class IndexFactory
|
18
|
+
|
19
|
+
include Meta
|
20
|
+
|
21
|
+
attr_writer :collection
|
22
|
+
|
23
|
+
def initialize(*options, &block)
|
24
|
+
initialize_with_options(options, [:collection])
|
25
|
+
self.instance_eval(&block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
wrapper do
|
30
|
+
ranges.to_s + lucenes.to_s
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def range(*targets)
|
35
|
+
@ranges ||= Array.new
|
36
|
+
targets.each do |target|
|
37
|
+
if target.is_a?(Hash) then
|
38
|
+
target.each do |key, value|
|
39
|
+
tgt_name = key.to_s
|
40
|
+
tgt_type = tgt_name.index('/') ? 'path' : 'qname'
|
41
|
+
index_type = type_convert(value)
|
42
|
+
@ranges << %|<create #{tgt_type}="#{tgt_name}" type="#{index_type}"/>|
|
43
|
+
end
|
44
|
+
elsif target.respond_to?(:to_s) then
|
45
|
+
tgt_name = target.to_s
|
46
|
+
tgt_type = tgt_name.index('/') ? 'path' : 'qname'
|
47
|
+
@ranges << %|<create #{tgt_type}="#{tgt_name}"/>|
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def lucene(*targets)
|
53
|
+
@lucenes ||= Array.new
|
54
|
+
targets.each do |target|
|
55
|
+
if target.is_a?(Hash) then
|
56
|
+
target.each do |key, value|
|
57
|
+
tgt_name = key.to_s
|
58
|
+
tgt_type = tgt_name.index('/') ? 'path' : 'qname'
|
59
|
+
index_type = type_convert(value)
|
60
|
+
@lucenes << %|<text #{tgt_type}="#{tgt_name}" type="#{index_type}"/>|
|
61
|
+
end
|
62
|
+
elsif target.respond_to?(:to_s) then
|
63
|
+
tgt_name = target.to_s
|
64
|
+
tgt_type = tgt_name.index('/') ? 'path' : 'qname'
|
65
|
+
@lucenes << %|<text #{tgt_type}="#{tgt_name}"/>|
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def collection(col = nil)
|
71
|
+
@collection = col if col
|
72
|
+
return @collection
|
73
|
+
end
|
74
|
+
|
75
|
+
def configure
|
76
|
+
raise "Specify a collection" unless @collection
|
77
|
+
col = collection.create_collection(configuration_collection_name)
|
78
|
+
res = Resource.new(:parent => col, :name => configuration_file, :xml => to_s)
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def ranges
|
84
|
+
@ranges && @ranges.join('')
|
85
|
+
end
|
86
|
+
|
87
|
+
def lucenes
|
88
|
+
if @lucenes then
|
89
|
+
%|<lucene><analyzer class="org.apache.lucene.analysis.standard.StandardAnalyzer"/><analyzer id="ws" class="org.apache.lucene.analysis.WhitespaceAnalyzer"/>#{ @lucenes.join('') }</lucene>|
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def wrapper
|
94
|
+
%|<collection xmlns="http://exist-db.org/collection-config/1.0"><index>| +
|
95
|
+
yield +
|
96
|
+
%|</index></collection>|
|
97
|
+
end
|
98
|
+
|
99
|
+
def type_convert(data_type)
|
100
|
+
data_type = data_type.to_s
|
101
|
+
{
|
102
|
+
'String' => 'xs:string',
|
103
|
+
'Fixnum' => 'xs:int',
|
104
|
+
'Bignum' => 'xs:int',
|
105
|
+
'Numeric' => 'xs:int',
|
106
|
+
'Float' => 'xs:float'
|
107
|
+
}[data_type]
|
108
|
+
end
|
109
|
+
|
110
|
+
def configuration_file
|
111
|
+
'collection.xconf'
|
112
|
+
end
|
113
|
+
|
114
|
+
def configuration_collection_name
|
115
|
+
"/db/system/config#{@collection}" if @collection
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
data/lib/existdb/meta.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module ExistDB
|
2
|
+
module Meta
|
3
|
+
# Metaprogramming conviences
|
4
|
+
|
5
|
+
def initialize_with_options(options, ordered_options)
|
6
|
+
# Usage:
|
7
|
+
# class MyClass
|
8
|
+
# include Meta
|
9
|
+
# attr_accessor :opt1, :opt2
|
10
|
+
# def initialize(*options)
|
11
|
+
# initialize_with_options(options, [:opt1, :opt2])
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# obj = MyClass.new(:opt1 => 'foo', :opt2 => 'bar')
|
16
|
+
# # OR
|
17
|
+
# obj = MyClass.new('foo', 'bar')
|
18
|
+
# # MyClass.new now accepts named or ordered params!
|
19
|
+
named_or_ordered_options(options, ordered_options).each do |key, value|
|
20
|
+
if key.is_a?(Symbol) or key.is_a?(String) then
|
21
|
+
key = "#{key}=".to_sym
|
22
|
+
self.send(key, value) if self.respond_to?(key)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
def named_or_ordered_options(options, ordered_options)
|
29
|
+
hash = Hash.new
|
30
|
+
options.each_with_index do |option, i|
|
31
|
+
if option.is_a?(Hash) then
|
32
|
+
hash.merge!(option)
|
33
|
+
else
|
34
|
+
key = ordered_options[i]
|
35
|
+
hash[key] = option
|
36
|
+
end
|
37
|
+
end
|
38
|
+
return hash
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module ExistDB
|
2
|
+
module Resource
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def new(*options)
|
6
|
+
if options.size == 1 and not options.first.is_a?(Hash) then
|
7
|
+
obj = options.first
|
8
|
+
ClassWrap[obj]
|
9
|
+
else
|
10
|
+
if options.any?{|opt| opt.is_a?(Hash) && ( opt[:xml] || opt[:type] == 'XMLResource' ) } then
|
11
|
+
Xml.new(*options)
|
12
|
+
elsif options.any?{|opt| opt.is_a?(Hash) && ( opt[:binary] || opt[:type] == 'BinaryResource' ) } then
|
13
|
+
Binary.new(*options)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Base
|
20
|
+
extend ClassWrappingForwardable
|
21
|
+
delegate_to_java(
|
22
|
+
:content= => :setContent,
|
23
|
+
:content => :getContent,
|
24
|
+
:length => :getContentLength,
|
25
|
+
:created => :getCreationTime,
|
26
|
+
:last_modified => :getLastModificationTime,
|
27
|
+
:resource_type => :getResourceType,
|
28
|
+
:parent => :getParentCollection,
|
29
|
+
:name => :getId
|
30
|
+
)
|
31
|
+
alias :to_s :content
|
32
|
+
alias :size :length
|
33
|
+
|
34
|
+
def initialize(*opts)
|
35
|
+
options = Hash.new
|
36
|
+
if opts.size == 1 and not opts.first.is_a?(Hash) then
|
37
|
+
@obj = opts.first
|
38
|
+
else
|
39
|
+
opts.each do |opt|
|
40
|
+
if opt.is_a?(Hash) then
|
41
|
+
options.merge!(opt)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
collection = ClassUnwrap[ options[:parent] ]
|
46
|
+
data = nil
|
47
|
+
if options[:binary]
|
48
|
+
type = "BinaryResource"
|
49
|
+
data = options[:binary]
|
50
|
+
elsif options[:xml]
|
51
|
+
type = "XMLResource"
|
52
|
+
data = options[:xml]
|
53
|
+
else
|
54
|
+
type = options[:type]
|
55
|
+
data = options[:content]
|
56
|
+
end
|
57
|
+
type ||= "XMLResource"
|
58
|
+
|
59
|
+
@obj = collection.createResource(options[:name], type)
|
60
|
+
if data then
|
61
|
+
self.content = data
|
62
|
+
self.save
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
def inspect
|
69
|
+
"#<#{self.class}:0x#{self.hash.to_s(16)} name=#{self.name.inspect}>"
|
70
|
+
end
|
71
|
+
|
72
|
+
def save
|
73
|
+
collection = @obj.getParentCollection
|
74
|
+
collection.storeResource( @obj )
|
75
|
+
true
|
76
|
+
end
|
77
|
+
|
78
|
+
def delete
|
79
|
+
parent.delete(self)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ExistDB
|
2
|
+
module Resource
|
3
|
+
class Binary < Base
|
4
|
+
|
5
|
+
def content=(data)
|
6
|
+
bytes = data.to_s.to_java_bytes
|
7
|
+
@obj.setContent(bytes)
|
8
|
+
end
|
9
|
+
|
10
|
+
def content
|
11
|
+
to_io.read
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_io
|
15
|
+
input_stream = @obj.getStreamContent
|
16
|
+
return Java.java_to_ruby(org.jruby.RubyIO.new(JRuby.runtime, input_stream).java_object)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ExistDB
|
2
|
+
module Resource
|
3
|
+
class Xml < Base
|
4
|
+
|
5
|
+
def xquery(*opts)
|
6
|
+
parent.xquery.query(self, *opts)
|
7
|
+
end
|
8
|
+
|
9
|
+
def compile(*opts)
|
10
|
+
parent.xquery.compile(*opts)
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(*opts)
|
14
|
+
parent.xquery.execute(self, *opts)
|
15
|
+
end
|
16
|
+
|
17
|
+
def dom
|
18
|
+
@obj.getContentAsDOM
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module ExistDB
|
2
|
+
class ResourceSet
|
3
|
+
|
4
|
+
include Enumerable
|
5
|
+
extend ClassWrappingForwardable
|
6
|
+
delegate_to_java(
|
7
|
+
:size => :getSize,
|
8
|
+
:[] => :getResource,
|
9
|
+
:join => :getMembersAsResource,
|
10
|
+
:add => :addResource
|
11
|
+
)
|
12
|
+
|
13
|
+
def initialize(java_obj)
|
14
|
+
@obj = java_obj
|
15
|
+
end
|
16
|
+
|
17
|
+
def each
|
18
|
+
for i in (0 .. (size - 1))
|
19
|
+
yield self[i]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class << self
|
24
|
+
def [](*resources)
|
25
|
+
set = new( org.exist.xmldb.MapResourceSet.new )
|
26
|
+
resources.each do |resource|
|
27
|
+
set.add(resource)
|
28
|
+
end
|
29
|
+
return set
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
module ExistDB
|
2
|
+
module XQLFactory
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
# Shorthand for ExistDB::XQLFactory::XQLFactory.new( options ).xquery
|
7
|
+
|
8
|
+
def Build(*opts, &block)
|
9
|
+
XQLFactory.new(*opts, &block).xquery
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# This is used by the XQLFactory for remapping nodes
|
14
|
+
#
|
15
|
+
# This could possibly be simplified by using XSLT if there was a Ruby DSL for XSLT
|
16
|
+
#
|
17
|
+
# The primary goal is to keep the barrier to entry low for rubyists who have limited XML experience
|
18
|
+
class Path
|
19
|
+
|
20
|
+
attr_reader :to_s, :to_a
|
21
|
+
def initialize(path)
|
22
|
+
if path.is_a?(String) then
|
23
|
+
@to_s = path
|
24
|
+
@to_a = path.split('/')
|
25
|
+
elsif path.is_a?(Array) then
|
26
|
+
@to_s = path.join('/')
|
27
|
+
@to_a = path
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def name
|
32
|
+
@to_a.last
|
33
|
+
end
|
34
|
+
|
35
|
+
def context
|
36
|
+
@to_a[0..-2]
|
37
|
+
end
|
38
|
+
|
39
|
+
def <=>(path)
|
40
|
+
to_a <=> path.to_a
|
41
|
+
end
|
42
|
+
|
43
|
+
def common(path)
|
44
|
+
a = Array.new
|
45
|
+
self.to_a.each_index do |i|
|
46
|
+
if self.to_a[i].nil? or
|
47
|
+
path.to_a[i].nil? or
|
48
|
+
self.to_a[i] != path.to_a[i] then
|
49
|
+
break
|
50
|
+
else
|
51
|
+
a << self.to_a[i]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
a << ''
|
55
|
+
return Path.new(a)
|
56
|
+
end
|
57
|
+
|
58
|
+
def switch_context(path)
|
59
|
+
output = String.new
|
60
|
+
common_path = self.common(path)
|
61
|
+
diff = self.context.size - common_path.context.size
|
62
|
+
if diff > 0 then
|
63
|
+
output << self.context.last(diff).reverse.map{ |node| "</#{node}>"}.join
|
64
|
+
end
|
65
|
+
diff = path.context.size - common_path.context.size
|
66
|
+
if diff > 0 then
|
67
|
+
output << path.context.last(diff).map{ |node| "<#{node}>"}.join
|
68
|
+
end
|
69
|
+
return output
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
# This is an attempt to create a Ruby DSL for the Most Common XQuery use cases.
|
75
|
+
class XQLFactory
|
76
|
+
|
77
|
+
include Meta
|
78
|
+
attr_accessor :start, :max, :doc,
|
79
|
+
:node_xpath, :sort, :return_attributes,
|
80
|
+
:return_tag, :node_remap, :search, :ftquery
|
81
|
+
|
82
|
+
# Accepts Ordered or Named Parameters for any of the attr_accessors
|
83
|
+
#
|
84
|
+
# Ordered Options -- :doc, :start, :max, :sort
|
85
|
+
#
|
86
|
+
# E.g.
|
87
|
+
#
|
88
|
+
# <tt>
|
89
|
+
# xql = XQLFactory.new("doc('http://example.com')", 5, 10, :node_xpath => '//a').xquery
|
90
|
+
# </tt>
|
91
|
+
#
|
92
|
+
# Would create an XQuery statement to find the fifth through tenth links on example.com
|
93
|
+
#
|
94
|
+
# See also ExistDB::XQLFactory.Build as a shorthand way of calling this.
|
95
|
+
|
96
|
+
def initialize(*options)
|
97
|
+
initialize_with_options(options, [:doc, :start, :max, :sort])
|
98
|
+
yield self if block_given?
|
99
|
+
end
|
100
|
+
|
101
|
+
def limit_statement
|
102
|
+
if start or max then
|
103
|
+
"let $scope := subsequence($scope, #{start || 1}, #{max || 'count($scope)'})\n"
|
104
|
+
else
|
105
|
+
''
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def sort_statement
|
110
|
+
if sort then
|
111
|
+
if sort.is_a?(String) then
|
112
|
+
"order by $node/#{sort}"
|
113
|
+
elsif sort.is_a?(Hash) then
|
114
|
+
"order by $node/#{sort.keys.first} #{sort_direction(sort.values.first)}"
|
115
|
+
elsif sort.is_a?(Array) then
|
116
|
+
"order by " + sort.map{ |h| "$node/#{h.keys.first} #{sort_direction(h.values.first)}" }.join(', ')
|
117
|
+
end
|
118
|
+
else
|
119
|
+
''
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def sort_direction(dir)
|
124
|
+
dir = dir.to_s.downcase
|
125
|
+
if %w|asc ascending|.include?(dir)
|
126
|
+
return :ascending
|
127
|
+
elsif %|desc descending|.include?(dir)
|
128
|
+
return :descending
|
129
|
+
else
|
130
|
+
return :ascending
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def search_statement
|
135
|
+
"[contains(., #{ search.inspect })]" if search
|
136
|
+
"[ft:query(., #{ ftquery.inspect })]" if ftquery
|
137
|
+
end
|
138
|
+
|
139
|
+
def init_statement
|
140
|
+
raise "doc attribute required" if doc.nil? or doc.to_s.empty?
|
141
|
+
raise "node_xpath attribute required" if node_xpath.nil? or node_xpath.empty?
|
142
|
+
"let $scope := for $node in #{doc if doc.is_a?(String)}#{node_xpath}#{search_statement} #{sort_statement} return $node\n"
|
143
|
+
end
|
144
|
+
|
145
|
+
def return_tag
|
146
|
+
@return_tag || 'xml'
|
147
|
+
end
|
148
|
+
|
149
|
+
def return_tag=(tag)
|
150
|
+
@return_tag = tag
|
151
|
+
end
|
152
|
+
|
153
|
+
def return_statement
|
154
|
+
if return_attributes_statement.empty? and @return_tag.nil? then
|
155
|
+
"return $scope"
|
156
|
+
else
|
157
|
+
"return <#{return_tag}#{return_attributes_statement}> { $scope } </#{return_tag}>"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def return_attributes_statement
|
162
|
+
return '' if return_attributes.nil? or return_attributes.empty?
|
163
|
+
' ' + return_attributes.keys.map{ |key|
|
164
|
+
"#{key}=#{return_attributes[key].to_s.inspect}"
|
165
|
+
}.join(' ')
|
166
|
+
end
|
167
|
+
|
168
|
+
def node_remap_statement
|
169
|
+
return '' if node_remap.nil? or node_remap.empty?
|
170
|
+
output = "let $scope := for $node in $scope return\n"
|
171
|
+
current_context = Path.new('')
|
172
|
+
node_remap.to_a.map{|a|
|
173
|
+
a[1] = Path.new(a[1]); a
|
174
|
+
}.sort.each do |a|
|
175
|
+
(src_path, dest_path) = a
|
176
|
+
output << current_context.switch_context(dest_path)
|
177
|
+
current_context = dest_path
|
178
|
+
output << "<#{dest_path.name}>{ $node#{src_path} }</#{dest_path.name}>"
|
179
|
+
end
|
180
|
+
output << current_context.switch_context( Path.new('') )
|
181
|
+
output << "\n"
|
182
|
+
return output
|
183
|
+
end
|
184
|
+
|
185
|
+
def xquery
|
186
|
+
init_statement + limit_statement + node_remap_statement + return_statement
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|