rubyuno 0.3.0
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.
- data/.gitignore +20 -0
- data/CHANGES +16 -0
- data/Gemfile +4 -0
- data/LICENSE +202 -0
- data/README +46 -0
- data/Rakefile +603 -0
- data/ext/rubyuno/adapter.cxx +271 -0
- data/ext/rubyuno/extconf.rb +125 -0
- data/ext/rubyuno/libruno.def +26 -0
- data/ext/rubyuno/loader.cxx +184 -0
- data/ext/rubyuno/module.cxx +1263 -0
- data/ext/rubyuno/rubyuno.hxx +263 -0
- data/ext/rubyuno/runo.def +2 -0
- data/ext/rubyuno/runtime.cxx +524 -0
- data/ext/rubyuno/string.cxx +252 -0
- data/ext/rubyuno/type.cxx +358 -0
- data/lib/rubyloader.rb +302 -0
- data/lib/rubyscriptprovider.rb +1025 -0
- data/lib/rubyuno.rb +20 -0
- data/lib/rubyuno/uno.rb +105 -0
- data/lib/rubyuno/uno/connector.rb +97 -0
- data/lib/rubyuno/version.rb +3 -0
- data/rubyuno.gemspec +17 -0
- data/sample/calc-chart.rb +66 -0
- data/sample/dialog_listener.rb +70 -0
- data/sample/filter-names.rb +123 -0
- data/sample/inputbox.rb +74 -0
- data/sample/mri.rb +17 -0
- data/sample/open-doc1.rb +20 -0
- metadata +89 -0
data/lib/rubyloader.rb
ADDED
@@ -0,0 +1,302 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
#
|
3
|
+
# Copyright 2011 Tsutomu Uchino
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
17
|
+
|
18
|
+
module RubyLoader
|
19
|
+
NAME = "RubyLoader"
|
20
|
+
|
21
|
+
LOADER_DEBUG_FLAG = "#{RubyLoader::NAME}Debug"
|
22
|
+
LOADER_DEBUG_OUTPUT = "#{LOADER_DEBUG_FLAG}Output"
|
23
|
+
LOG_DEFAULT_PATH = "$(user)/temp/#{LOADER_DEBUG_FLAG}.txt"
|
24
|
+
if ENV[LOADER_DEBUG_OUTPUT]
|
25
|
+
require 'logger'
|
26
|
+
@@log = Logger.new(
|
27
|
+
case ENV.fetch(LOADER_DEBUG_OUTPUT, "STDOUT")
|
28
|
+
when "STDOUT"
|
29
|
+
STDOUT
|
30
|
+
when "STDERR"
|
31
|
+
STDERR
|
32
|
+
when "FILE"
|
33
|
+
Uno.to_system_path(Runo.get_component_context.getServiceManager.
|
34
|
+
createInstanceWithContext("com.sun.star.util.PathSubstitution",
|
35
|
+
Runo.get_component_context).
|
36
|
+
substituteVariables(LOG_DEFAULT_PATH, true))
|
37
|
+
else
|
38
|
+
ENV.fetch(LOADER_DEBUG_OUTPUT)
|
39
|
+
end
|
40
|
+
)
|
41
|
+
@@log.level = Logger.const_get(
|
42
|
+
ENV.fetch(LOADER_DEBUG_FLAG, "ERROR").intern)
|
43
|
+
else
|
44
|
+
class DummyLogger
|
45
|
+
def null_out(progname=nil, &b)
|
46
|
+
end
|
47
|
+
|
48
|
+
def out(progname=nil, &b)
|
49
|
+
if b
|
50
|
+
puts "#{progname} -- #{b()}"
|
51
|
+
else
|
52
|
+
puts b()
|
53
|
+
end
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
alias :log :null_out
|
58
|
+
alias :debug :null_out
|
59
|
+
alias :warn :null_out
|
60
|
+
alias :error :out
|
61
|
+
alias :fatal :out
|
62
|
+
end
|
63
|
+
@@log = DummyLogger.new
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.log
|
67
|
+
return @@log
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
module Uno
|
72
|
+
RRuno.uno_require 'com.sun.star.lang.XServiceInfo'
|
73
|
+
|
74
|
+
# All UNO components should be include UnoComponentBase module
|
75
|
+
# to its class and define two constants for it. If your class
|
76
|
+
# include css.lang.XServiceInfo module also, they are used to provide
|
77
|
+
# service information of your component.
|
78
|
+
# IMPLE_NAME: implementation name of the component, have to be defined
|
79
|
+
# on herself.
|
80
|
+
# SERVICE_NAMES: Array of supported service names.
|
81
|
+
module UnoComponentBase
|
82
|
+
# IMPLE_NAME = ""
|
83
|
+
# SERVICE_NAMES = []
|
84
|
+
end
|
85
|
+
|
86
|
+
# Provides XServiceInfo interface from two constants.
|
87
|
+
# IMPLE_NAME is its implementation name in String.
|
88
|
+
# SERVICE_NAMES is supported service names of the component
|
89
|
+
# in Array of String.
|
90
|
+
module Com::Sun::Star::Lang::XServiceInfo
|
91
|
+
def getImplementationName
|
92
|
+
return self.class::IMPLE_NAME
|
93
|
+
end
|
94
|
+
|
95
|
+
def supportsService(name)
|
96
|
+
return self.class::SERVICE_NAMES.include? name
|
97
|
+
end
|
98
|
+
|
99
|
+
def getSupportedServiceNames
|
100
|
+
return self.class::SERVICE_NAMES
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Loads implementations which are written in Ruby.
|
106
|
+
module RubyLoader
|
107
|
+
|
108
|
+
Runo.uno_require 'com.sun.star.uno.Exception'
|
109
|
+
Runo.uno_require 'com.sun.star.lang.XSingleComponentFactory'
|
110
|
+
Runo.uno_require 'com.sun.star.loader.XImplementationLoader'
|
111
|
+
|
112
|
+
def self.error_to_str(e)
|
113
|
+
return %Q!#{e}\n#{e.backtrace.join("\n")}\n!
|
114
|
+
end
|
115
|
+
|
116
|
+
# Component factory class wraps your class which implements
|
117
|
+
# UNO component.
|
118
|
+
class SingleComponentFactory
|
119
|
+
include Uno::UnoBase
|
120
|
+
include Runo::Com::Sun::Star::Lang::XServiceInfo
|
121
|
+
include Runo::Com::Sun::Star::Lang::XSingleComponentFactory
|
122
|
+
|
123
|
+
def initialize(klass, imple_name, url)
|
124
|
+
@klass = klass
|
125
|
+
@imple_name = imple_name
|
126
|
+
@url = url
|
127
|
+
end
|
128
|
+
|
129
|
+
# XSingleComponentFactory
|
130
|
+
def createInstanceWithContext(ctx)
|
131
|
+
begin
|
132
|
+
load_class unless @klass
|
133
|
+
return @klass.new(ctx)
|
134
|
+
rescue => e
|
135
|
+
RubyLoader.log.error(@klass.to_s) {
|
136
|
+
"Error raised at #{self.class.name}##{__method__}\n" +
|
137
|
+
RubyLoader.error_to_str(e)}
|
138
|
+
raise
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Generaly, this method instantiate the class and
|
143
|
+
# calls css.lang.XInitialization#initialize method.
|
144
|
+
# But Ruby's initialize method is special one.
|
145
|
+
# Therefore additional arguments are passed as second one.
|
146
|
+
def createInstanceWithArgumentsAndContext(args, ctx)
|
147
|
+
begin
|
148
|
+
load_class unless @klass
|
149
|
+
return @klass.new(ctx, args)
|
150
|
+
rescue => e
|
151
|
+
RubyLoader.log.error(@klass.to_s) {
|
152
|
+
"Error raised at #{self.class.name}##{__method__}\n" +
|
153
|
+
RubyLoader.error_to_str(e)}
|
154
|
+
raise
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# XServiceInfo
|
159
|
+
|
160
|
+
def getImplementationName
|
161
|
+
return @klass::IMPLE_NAME if @klass
|
162
|
+
return @imple_name
|
163
|
+
end
|
164
|
+
|
165
|
+
def supportsService(name)
|
166
|
+
return @klass::SERVICE_NAMES.include?(name)
|
167
|
+
end
|
168
|
+
|
169
|
+
def getSupportedServiceNames
|
170
|
+
return @klass::SERVICE_NAMES if @klass
|
171
|
+
return []
|
172
|
+
end
|
173
|
+
|
174
|
+
private
|
175
|
+
|
176
|
+
# load_class -> Class
|
177
|
+
# Load class from the file which implements components.
|
178
|
+
def load_class
|
179
|
+
begin
|
180
|
+
@klass = REGISTERED.get_imple(@klass, @url, @imple_name)
|
181
|
+
rescue => e
|
182
|
+
# missing file, syntax error, load error and so on
|
183
|
+
message = "#{@imple_name} was not found in #{@url}. " +
|
184
|
+
RubyLoader.error_to_str(e)
|
185
|
+
puts message
|
186
|
+
RubyLoader.log.error(@klass.to_s) {message}
|
187
|
+
ex = Runo::Com::Sun::Star::Uno::Exception.new(message, nil)
|
188
|
+
raise Uno::UnoError, ex
|
189
|
+
end
|
190
|
+
unless @klass
|
191
|
+
ex = Runo::Com::Sun::Star::Uno::Exception.new(
|
192
|
+
"#{@imple_name} was not found in #{@url}", nil)
|
193
|
+
raise Uno::UnoError, ex
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# keeps loaded modules of UNO components
|
199
|
+
REGISTERED = {}
|
200
|
+
class << REGISTERED
|
201
|
+
|
202
|
+
EXPAND_PROTOCOL = "vnd.sun.star.expand:"
|
203
|
+
|
204
|
+
# get_imple(ctx, url, imple_name) -> Class
|
205
|
+
# Load file and get implementation as an class.
|
206
|
+
def get_imple(ctx, url, imple_name)
|
207
|
+
klass = find_imple(imple_name, url)
|
208
|
+
unless klass
|
209
|
+
mod = load_as_module(ctx, url)
|
210
|
+
register_module(url, mod)
|
211
|
+
klass = find_imple(url, imple_name)
|
212
|
+
end
|
213
|
+
return klass
|
214
|
+
end
|
215
|
+
|
216
|
+
private
|
217
|
+
|
218
|
+
# load_as_module(ctx, url) -> Module
|
219
|
+
# Loads file content into a module with eval.
|
220
|
+
def load_as_module(ctx, url)
|
221
|
+
path = get_path(ctx, url)
|
222
|
+
mod = Module.new
|
223
|
+
mod.module_eval(IO.read(path), path)
|
224
|
+
return mod
|
225
|
+
end
|
226
|
+
|
227
|
+
# register_module(url, mod) -> obj
|
228
|
+
# Register new module at url.
|
229
|
+
def register_module(url, mod)
|
230
|
+
REGISTERED[url] = mod
|
231
|
+
end
|
232
|
+
|
233
|
+
# find_imple(url, imple_name) -> Class|nil
|
234
|
+
# Find implementation from the file.
|
235
|
+
def find_imple(url, imple_name)
|
236
|
+
klass = nil
|
237
|
+
mod = REGISTERED[url]
|
238
|
+
if mod
|
239
|
+
get_component_classes(mod).each do |imple|
|
240
|
+
if imple.const_defined?(:IMPLE_NAME) &&
|
241
|
+
imple.const_defined?(:SERVICE_NAMES) &&
|
242
|
+
imple.const_get(:IMPLE_NAME) == imple_name
|
243
|
+
klass = imple
|
244
|
+
break
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
return klass
|
249
|
+
end
|
250
|
+
|
251
|
+
# get_component_classes(mod) -> [Class]
|
252
|
+
# Find all classes includes Uno::ComponentBase module from module.
|
253
|
+
def get_component_classes(mod)
|
254
|
+
klasses = []
|
255
|
+
mod.constants.each do |item|
|
256
|
+
c = mod.const_get(item)
|
257
|
+
if c.instance_of?(Module)
|
258
|
+
klasses.concat(get_component_classes(c))
|
259
|
+
elsif c.instance_of?(Class) &&
|
260
|
+
c.ancestors.include?(Uno::UnoComponentBase)
|
261
|
+
klasses << c
|
262
|
+
end
|
263
|
+
end
|
264
|
+
return klasses
|
265
|
+
end
|
266
|
+
|
267
|
+
# get_path(ctx, url) -> String
|
268
|
+
# Make expanded url.
|
269
|
+
def get_path(ctx, url)
|
270
|
+
if url.start_with?(EXPAND_PROTOCOL)
|
271
|
+
url = ctx.getValueByName(
|
272
|
+
"/singletons/com.sun.star.util.theMacroExpander").expandMacros(url)
|
273
|
+
url[EXPAND_PROTOCOL] = ""
|
274
|
+
end
|
275
|
+
return Uno.to_system_path(url)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
# This class is refered from C++ loader implementation.
|
280
|
+
class RubyLoaderImpl
|
281
|
+
include Uno::UnoBase
|
282
|
+
include Runo::Com::Sun::Star::Loader::XImplementationLoader
|
283
|
+
include Runo::Com::Sun::Star::Lang::XServiceInfo
|
284
|
+
|
285
|
+
IMPLE_NAME = "mytools.loader.Ruby"
|
286
|
+
SERVICE_NAMES = ["com.sun.star.loader.Ruby"]
|
287
|
+
|
288
|
+
def initialize(ctx)
|
289
|
+
@ctx = ctx
|
290
|
+
end
|
291
|
+
|
292
|
+
# XImplementationLoader
|
293
|
+
def activate(imple_name, ignored, url, key)
|
294
|
+
return SingleComponentFactory.new(nil, imple_name, url)
|
295
|
+
end
|
296
|
+
|
297
|
+
# this method is no longer required
|
298
|
+
def writeRegistryInfo(key, ignored, url)
|
299
|
+
return true
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
@@ -0,0 +1,1025 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Copyright 2011 Tsutomu Uchino
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
17
|
+
|
18
|
+
#
|
19
|
+
# When you want to execute a macro written in Ruby,
|
20
|
+
# it should be placed under user_profile/Scripts/ruby directory.
|
21
|
+
module RubyScriptProvider
|
22
|
+
LANGUAGE = "Ruby"
|
23
|
+
NAME = "#{LANGUAGE}ScriptProvider"
|
24
|
+
RB_EXTENSION = ".rb"
|
25
|
+
end
|
26
|
+
|
27
|
+
module LoggerForRubyScriptProvider
|
28
|
+
|
29
|
+
PROVIDER_DEBUG_FLAG = "#{RubyScriptProvider::NAME}Debug"
|
30
|
+
PROVIDER_DEBUG_OUTPUT = "#{PROVIDER_DEBUG_FLAG}Output"
|
31
|
+
LOG_DEFAULT_PATH = "$(user)/temp/#{PROVIDER_DEBUG_OUTPUT}.txt"
|
32
|
+
|
33
|
+
require 'logger'
|
34
|
+
@@log = Logger.new(
|
35
|
+
case ENV.fetch(LOADER_DEBUG_OUTPUT, "STDOUT")
|
36
|
+
when "STDOUT"
|
37
|
+
STDOUT
|
38
|
+
when "FILE"
|
39
|
+
Uno.to_system_path(
|
40
|
+
Runo.get_component_context.getServiceManager.
|
41
|
+
createInstanceWithContext(
|
42
|
+
"com.sun.star.util.PathSubstitution",
|
43
|
+
Runo.get_component_context).
|
44
|
+
substituteVariables(LOG_DEFAULT_PATH, true))
|
45
|
+
else
|
46
|
+
ENV.fetch(LOADER_DEBUG_OUTPUT)
|
47
|
+
end
|
48
|
+
)
|
49
|
+
@@log.level = Logger.const_get(
|
50
|
+
ENV.fetch(LOADER_DEBUG_FLAG, "ERROR").intern)
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
module RubyScriptProvider
|
55
|
+
# keeps loaded scripts
|
56
|
+
module RubyScripts
|
57
|
+
@@scripts = {}
|
58
|
+
|
59
|
+
# get(name, datetime) -> RubyScript|nil
|
60
|
+
# Try to get script. If the script is updated,
|
61
|
+
# nil is returned. Otherwise found script is returned.
|
62
|
+
def self.get(name, datetime)
|
63
|
+
item = @@scripts[name]
|
64
|
+
if item
|
65
|
+
if item[:timestamp] >= datetime
|
66
|
+
return item[:module]
|
67
|
+
else
|
68
|
+
@@scripts.delete(name)
|
69
|
+
return nil
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# add(name, datetime, capsule) -> Hash
|
75
|
+
# Register new script with update time.
|
76
|
+
def self.add(name, datetime, capsule)
|
77
|
+
@@scripts[name] = {:timestamp => datetime, :module => capsule}
|
78
|
+
end
|
79
|
+
|
80
|
+
# delete(name) -> Hash
|
81
|
+
# Delete specific script.
|
82
|
+
def self.delete(name)
|
83
|
+
@@scripts.delete(name)
|
84
|
+
end
|
85
|
+
|
86
|
+
# get_script(storage, script_context, url, uri) -> RubyScript
|
87
|
+
# Creates capsulated script.
|
88
|
+
def self.get_script(storage, script_context, url, uri)
|
89
|
+
script = nil
|
90
|
+
capsule = nil
|
91
|
+
file_name, func_name = url.to_url(uri)
|
92
|
+
if storage.exists?(file_name)
|
93
|
+
if file_name.start_with?(DOC_PROTOCOL)
|
94
|
+
t = Time.new # avoide illegal error
|
95
|
+
else
|
96
|
+
t = RubyScripts.datetime_to_time(storage.modified_time(file_name))
|
97
|
+
end
|
98
|
+
capsule = RubyScripts.get(uri, t)
|
99
|
+
unless capsule
|
100
|
+
klass = Class.new
|
101
|
+
klass.const_set(:XSCRIPTCONTEXT, script_context)
|
102
|
+
text = storage.file_content(file_name)
|
103
|
+
klass.class_eval(text, Uno.to_system_path(file_name))
|
104
|
+
capsule = klass.new
|
105
|
+
RubyScripts.add(uri, t, capsule)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
unless capsule
|
109
|
+
ex = ScriptFrameworkErrorException.new(
|
110
|
+
"script not found", nil, uri, LANGUAGE, 2)
|
111
|
+
raise ex
|
112
|
+
end
|
113
|
+
if capsule.public_methods(false).include?(func_name.intern)
|
114
|
+
script = RubyScript.new(capsule, file_name, func_name)
|
115
|
+
end
|
116
|
+
return script
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
# datetime_to_time(dt) -> Time
|
122
|
+
# Convert css.util.DateTime to Time.
|
123
|
+
def self.datetime_to_time(dt)
|
124
|
+
return Time.local(dt.Year, dt.Month, dt.Day,
|
125
|
+
dt.Hours, dt.Minutes, dt.Seconds)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
Runo.uno_require 'com.sun.star.script.provider.XScriptContext'
|
130
|
+
|
131
|
+
# class for XSCRIPTCONTEXT constant
|
132
|
+
class ScriptContext
|
133
|
+
include Uno::UnoBase
|
134
|
+
include Runo::Com::Sun::Star::Script::Provider::XScriptContext
|
135
|
+
|
136
|
+
def initialize(component_context, document, inv)
|
137
|
+
@component_context = component_context
|
138
|
+
@document = document
|
139
|
+
@inv = inv
|
140
|
+
end
|
141
|
+
|
142
|
+
# XScriptContext
|
143
|
+
def getComponentContext
|
144
|
+
return @component_context
|
145
|
+
end
|
146
|
+
|
147
|
+
def getDocument
|
148
|
+
return @document if @document
|
149
|
+
return getDesktop.getCurrentComponent
|
150
|
+
end
|
151
|
+
|
152
|
+
def getDesktop
|
153
|
+
return @component_context.getServiceManager.createInstanceWithContext(
|
154
|
+
"com.sun.star.frame.Desktop", @component_context)
|
155
|
+
end
|
156
|
+
|
157
|
+
def getInvocationContext
|
158
|
+
return @inv
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
module RubyScriptProvider
|
165
|
+
|
166
|
+
DOC_PROTOCOL = "vnd.sun.star.tdoc:"
|
167
|
+
|
168
|
+
# Macro URL.
|
169
|
+
class URL
|
170
|
+
EXPAND_PROTOCOL = "vnd.sun.star.expand:"
|
171
|
+
|
172
|
+
def initialize(ctx, context, url)
|
173
|
+
@ctx = ctx
|
174
|
+
@context = context
|
175
|
+
@url = url
|
176
|
+
@url = helper.getRootStorageURI unless url
|
177
|
+
end
|
178
|
+
|
179
|
+
attr_reader :url, :context
|
180
|
+
alias :to_s :url
|
181
|
+
|
182
|
+
def create(url)
|
183
|
+
return RubyScriptProvider::URL.new(@ctx, @context, url)
|
184
|
+
end
|
185
|
+
|
186
|
+
def helper
|
187
|
+
return create_service("com.sun.star.script.provider.ScriptURIHelper",
|
188
|
+
[LANGUAGE, @context])
|
189
|
+
end
|
190
|
+
|
191
|
+
def absolutize
|
192
|
+
return Uno.absolutize(@url, @url) rescue nil
|
193
|
+
return @url
|
194
|
+
end
|
195
|
+
|
196
|
+
def transient?
|
197
|
+
return @url.start_with?(DOC_PROTOCOL)
|
198
|
+
end
|
199
|
+
|
200
|
+
def basename(ext="")
|
201
|
+
return File.basename(@url, ext)
|
202
|
+
end
|
203
|
+
|
204
|
+
def dirname
|
205
|
+
return File.dirname(@url)
|
206
|
+
end
|
207
|
+
|
208
|
+
def to_uri(function_name=nil)
|
209
|
+
url = @url
|
210
|
+
url += "$" + function_name if function_name
|
211
|
+
return helper.getScriptURI(url)
|
212
|
+
end
|
213
|
+
|
214
|
+
def join(a, b=nil)
|
215
|
+
if b
|
216
|
+
return a + b if a.end_with?("/")
|
217
|
+
return "#{a}/#{b}"
|
218
|
+
else
|
219
|
+
return @url + a if @url.end_with?("/")
|
220
|
+
return "#{@url}/#{a}"
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def expand(uri)
|
225
|
+
url = @ctx.getValueByName(
|
226
|
+
"/singletons/com.sun.star.util.theMacroExpander").expandMacros(uri)
|
227
|
+
url[EXPAND_PROTOCOL] = ""
|
228
|
+
url = Uno.absolutize(url, url) rescue nil
|
229
|
+
url += "/" if uri.end_with?("/")
|
230
|
+
return url
|
231
|
+
end
|
232
|
+
|
233
|
+
def to_url(uri)
|
234
|
+
url = helper.getStorageURI(uri)
|
235
|
+
n = url.rindex("$")
|
236
|
+
return url[0..n-1], url[n+1..-1]
|
237
|
+
end
|
238
|
+
|
239
|
+
private
|
240
|
+
|
241
|
+
# create_service(name, args=nil) -> obj
|
242
|
+
# Create instance of service.
|
243
|
+
def create_service(name, args=nil)
|
244
|
+
return @ctx.getServiceManager.createInstanceWithArgumentsAndContext(
|
245
|
+
name, args, @ctx) if args
|
246
|
+
return @ctx.getServiceManager.createInstanceWithContext(name, @ctx)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
# Wrapper for file access.
|
251
|
+
class Storage
|
252
|
+
YAML_EXTENSION = '.yml'
|
253
|
+
|
254
|
+
def initialize(ctx)
|
255
|
+
@ctx = ctx
|
256
|
+
@sfa = create_service("com.sun.star.ucb.SimpleFileAccess")
|
257
|
+
end
|
258
|
+
|
259
|
+
def modified_time(url)
|
260
|
+
return @sfa.getDateTimeModified(url.to_s)
|
261
|
+
end
|
262
|
+
|
263
|
+
def exists?(url)
|
264
|
+
return @sfa.exists(url.to_s)
|
265
|
+
end
|
266
|
+
|
267
|
+
def is_dir?(url)
|
268
|
+
return @sfa.isFolder(url.to_s)
|
269
|
+
end
|
270
|
+
|
271
|
+
def is_readonly?(url)
|
272
|
+
return @sfa.isReadOnly(url.to_s)
|
273
|
+
end
|
274
|
+
|
275
|
+
def rename(url, dest_url)
|
276
|
+
@sfa.move(url, dest_url)
|
277
|
+
end
|
278
|
+
|
279
|
+
def delete(url)
|
280
|
+
@sfa.kill(url.to_s)
|
281
|
+
return true
|
282
|
+
end
|
283
|
+
|
284
|
+
def dir_contents(url)
|
285
|
+
return @sfa.getFolderContents(url.to_s, true) if exists?(url)
|
286
|
+
return []
|
287
|
+
end
|
288
|
+
|
289
|
+
def dir_create(url)
|
290
|
+
@sfa.createFolder(url.to_s) unless exists?(url)
|
291
|
+
end
|
292
|
+
|
293
|
+
def file_create(url, name)
|
294
|
+
dir_create(url)
|
295
|
+
write_text(url.join(name))
|
296
|
+
end
|
297
|
+
|
298
|
+
def file_copy(source, dest, yaml=false)
|
299
|
+
source = source.to_s
|
300
|
+
dest = dest.to_s
|
301
|
+
if @sfa.exists(source)
|
302
|
+
source_input = @sfa.openFileRead(source)
|
303
|
+
@sfa.kill(dest) if @sfa.exists(dest)
|
304
|
+
@sfa.writeFile(dest, source_input)
|
305
|
+
source_input.closeInput
|
306
|
+
if yaml
|
307
|
+
yaml_source = source[0..-File.extname(source).length-1] +
|
308
|
+
YAML_EXTENSION
|
309
|
+
if @sfa.exists(yaml_source)
|
310
|
+
yaml_dest = dest[0..-File.extname(dest).length-1] +
|
311
|
+
YAML_EXTENSION
|
312
|
+
file_copy(yaml_source, yaml_dest)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
def write_text(url, text="")
|
319
|
+
file_url = url.to_s
|
320
|
+
text_output = create_service("com.sun.star.io.TextOutputStream")
|
321
|
+
if url.start_with?(DOC_PROTOCOL)
|
322
|
+
pipe = create_service("com.sun.star.io.Pipe")
|
323
|
+
text_output.setOutputStream(pipe)
|
324
|
+
text_output.writeString(text)
|
325
|
+
text_output.closeOutput
|
326
|
+
@sfa.writeFile(file_url, pipe)
|
327
|
+
pipe.closeInput
|
328
|
+
else
|
329
|
+
file_output = @sfa.openFileWrite(file_url)
|
330
|
+
text_output.setOutputStream(file_output)
|
331
|
+
text_output.writeString(text)
|
332
|
+
text_output.flush()
|
333
|
+
file_output.closeOutput
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
def choose_file
|
338
|
+
scripts_dir = create_service("com.sun.star.util.PathSubstitution")
|
339
|
+
.substituteVariables("$(user)/Scripts/#{LANGUAGE.downcase}", true)
|
340
|
+
picker = create_service("com.sun.star.ui.dialogs.FilePicker")
|
341
|
+
picker.appendFilter("All Files (*.*)", "*.*")
|
342
|
+
picker.appendFilter(
|
343
|
+
"#{LANGUAGE} Files (*#{RB_EXTENSION})", "*#{RB_EXTENSION}")
|
344
|
+
picker.setCurrentFilter("All Files (*.*)")
|
345
|
+
picker.setDisplayDirectory(scripts_dir)
|
346
|
+
picker.setMultiSelectionMode(false)
|
347
|
+
picker.getFiles[0] if picker.execute > 0
|
348
|
+
end
|
349
|
+
|
350
|
+
def file_content(url)
|
351
|
+
lines = []
|
352
|
+
text_input = create_service("com.sun.star.io.TextInputStream")
|
353
|
+
text_input.setInputStream(@sfa.openFileRead(url.to_s))
|
354
|
+
lines << text_input.readLine until text_input.isEOF
|
355
|
+
text_input.closeInput
|
356
|
+
return lines.join("\n")
|
357
|
+
end
|
358
|
+
|
359
|
+
def descriptions(url)
|
360
|
+
require 'yaml'
|
361
|
+
ret = nil
|
362
|
+
yaml_url = url.join(url.dirname,
|
363
|
+
url.basename(RB_EXTENSION) + YAML_EXTENSION)
|
364
|
+
if exists?(yaml_url)
|
365
|
+
text = file_content(yaml_url)
|
366
|
+
ret = YAML.load(text) rescue nil
|
367
|
+
end
|
368
|
+
ret = {} unless ret
|
369
|
+
return ret
|
370
|
+
end
|
371
|
+
|
372
|
+
# try to find public methods from sexp tree
|
373
|
+
def get_method_names(url)
|
374
|
+
require 'ripper'
|
375
|
+
text = file_content(url)
|
376
|
+
result = Ripper.sexp(text, url.to_s)
|
377
|
+
method_defs = []
|
378
|
+
if result.length == 2 && result[0] == :program
|
379
|
+
is_public = true
|
380
|
+
result[1].each do |item|
|
381
|
+
case item[0]
|
382
|
+
when :def
|
383
|
+
ident = find_item(item, :@ident)
|
384
|
+
if ident
|
385
|
+
if is_public
|
386
|
+
method_defs << ident
|
387
|
+
else
|
388
|
+
method_defs.delete(ident)
|
389
|
+
end
|
390
|
+
end
|
391
|
+
when :command
|
392
|
+
ident = find_item(item, :@ident)
|
393
|
+
case ident
|
394
|
+
when "private", "protected", "public"
|
395
|
+
block = find_item(item, :args_add_block)
|
396
|
+
items = get_symbol_literals(block)
|
397
|
+
if ident == "public"
|
398
|
+
method_defs.concat(items)
|
399
|
+
else
|
400
|
+
items.each do |i|
|
401
|
+
method_defs.delete(i)
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
when :var_ref
|
406
|
+
ident = find_item(item, :@ident)
|
407
|
+
is_public = ident == "public" if ident
|
408
|
+
end
|
409
|
+
end
|
410
|
+
method_defs.uniq!
|
411
|
+
end
|
412
|
+
return method_defs
|
413
|
+
end
|
414
|
+
|
415
|
+
private
|
416
|
+
|
417
|
+
def find_item(args, type)
|
418
|
+
found = args[1..-1].assoc(type)
|
419
|
+
return found[1] if found
|
420
|
+
end
|
421
|
+
|
422
|
+
def get_symbol_literals(args)
|
423
|
+
founds = []
|
424
|
+
args.each do |i|
|
425
|
+
if i[0] == :symbol_literal
|
426
|
+
ident = find_item(i[1], :@ident)
|
427
|
+
founds << ident if ident
|
428
|
+
end
|
429
|
+
end
|
430
|
+
return founds
|
431
|
+
end
|
432
|
+
|
433
|
+
def create_service(name)
|
434
|
+
return @ctx.getServiceManager.createInstanceWithContext(name, @ctx)
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
Runo.uno_require 'com.sun.star.task.XInteractionHandler'
|
439
|
+
Runo.uno_require 'com.sun.star.ucb.XProgressHandler'
|
440
|
+
Runo.uno_require 'com.sun.star.ucb.XCommandEnvironment'
|
441
|
+
|
442
|
+
class DummyInteractionHandler
|
443
|
+
include Uno::UnoBase
|
444
|
+
include Runo::Com::Sun::Star::Task::XInteractionHandler
|
445
|
+
|
446
|
+
def handle(request)
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
class DummyProgressHandler
|
451
|
+
include Uno::UnoBase
|
452
|
+
include Runo::Com::Sun::Star::Ucb::XProgressHandler
|
453
|
+
|
454
|
+
def push(status)
|
455
|
+
end
|
456
|
+
|
457
|
+
def update(status)
|
458
|
+
end
|
459
|
+
|
460
|
+
def pop
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
class CommandEnvironment
|
465
|
+
include Uno::UnoBase
|
466
|
+
include Runo::Com::Sun::Star::Ucb::XCommandEnvironment
|
467
|
+
|
468
|
+
def initialize
|
469
|
+
@interaction_handler = DummyInteractionHandler.new
|
470
|
+
@progress_handler = DummyProgressHandler.new
|
471
|
+
end
|
472
|
+
|
473
|
+
def getInteractionHandler
|
474
|
+
return @interaction_handler
|
475
|
+
end
|
476
|
+
|
477
|
+
def getProgressHandler
|
478
|
+
return @progress_handler
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
Property = Runo.uno_require 'com.sun.star.beans.Property'
|
483
|
+
Command = Runo.uno_require 'com.sun.star.ucb.Command'
|
484
|
+
|
485
|
+
# get_document_model(ctx, uri) -> obj
|
486
|
+
# Get document model from internal uri.
|
487
|
+
def self.get_document_model(ctx, uri)
|
488
|
+
doc = nil
|
489
|
+
smgr = ctx.getServiceManager
|
490
|
+
ucb = smgr.createInstanceWithArgumentsAndContext(
|
491
|
+
"com.sun.star.ucb.UniversalContentBroker", ["Local", "Office"], ctx)
|
492
|
+
identifier = ucb.createContentIdentifier(uri)
|
493
|
+
content = ucb.queryContent(identifier)
|
494
|
+
property = Property.new("DocumentModel", -1, Uno.get_type("void"), 1)
|
495
|
+
command = Command.new(
|
496
|
+
"getPropertyValues", -1,
|
497
|
+
Runo::Any.new("[]com.sun.star.beans.Property", [property]))
|
498
|
+
env = CommandEnvironment.new
|
499
|
+
begin
|
500
|
+
result_set = content.execute(command, 0, env)
|
501
|
+
doc = result_set.getObject(1, nil)
|
502
|
+
rescue => e
|
503
|
+
p e.to_s + "\n" + e.backtrace.join("\n") + "\n"
|
504
|
+
end
|
505
|
+
return doc
|
506
|
+
end
|
507
|
+
|
508
|
+
# get_context(ctx, doc) -> obj
|
509
|
+
# Get document context.
|
510
|
+
def self.get_context(ctx, doc)
|
511
|
+
return ctx.getServiceManager.createInstanceWithContext(
|
512
|
+
"com.sun.star.frame.TransientDocumentsDocumentContentFactory", ctx).
|
513
|
+
createDocumentContent(doc).getIdentifier.getContentIdentifier
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
module RubyScriptProvider
|
518
|
+
|
519
|
+
Runo.uno_require 'com.sun.star.beans.XPropertySet'
|
520
|
+
Runo.uno_require 'com.sun.star.container.XNameContainer'
|
521
|
+
Runo.uno_require 'com.sun.star.lang.XServiceInfo'
|
522
|
+
Runo.uno_require 'com.sun.star.script.browse.XBrowseNode'
|
523
|
+
Runo.uno_require 'com.sun.star.script.provider.XScriptProvider'
|
524
|
+
Runo.uno_require 'com.sun.star.script.provider.XScript'
|
525
|
+
Runo.uno_require 'com.sun.star.script.XInvocation'
|
526
|
+
ScriptFrameworkErrorException = Runo.uno_require(
|
527
|
+
"com.sun.star.script.provider.ScriptFrameworkErrorException")
|
528
|
+
RuntimeException = Runo.uno_require(
|
529
|
+
"com.sun.star.uno.RuntimeException")
|
530
|
+
BrowseNodeTypes = Runo.uno_require(
|
531
|
+
"com.sun.star.script.browse.BrowseNodeTypes")
|
532
|
+
|
533
|
+
# base node for all script nodes
|
534
|
+
class BaseNode
|
535
|
+
include Uno::UnoBase
|
536
|
+
include Runo::Com::Sun::Star::Script::Browse::XBrowseNode
|
537
|
+
include Runo::Com::Sun::Star::Script::XInvocation
|
538
|
+
include Runo::Com::Sun::Star::Beans::XPropertySet
|
539
|
+
|
540
|
+
NODE_TYPE = BrowseNodeTypes::CONTAINER
|
541
|
+
|
542
|
+
def initialize(url, storage, name)
|
543
|
+
@url = url
|
544
|
+
@storage = storage
|
545
|
+
@name = name
|
546
|
+
context = @url.context
|
547
|
+
@editable = (context == "user" || context.start_with?(DOC_PROTOCOL))
|
548
|
+
end
|
549
|
+
|
550
|
+
attr_reader :name, :editable
|
551
|
+
|
552
|
+
def update(name, url)
|
553
|
+
@name = name
|
554
|
+
@url = url
|
555
|
+
end
|
556
|
+
|
557
|
+
# XBrowseNode
|
558
|
+
alias :getName :name
|
559
|
+
|
560
|
+
def getChildNodes
|
561
|
+
return []
|
562
|
+
end
|
563
|
+
|
564
|
+
def hasChildNodes
|
565
|
+
return true
|
566
|
+
end
|
567
|
+
|
568
|
+
def getType
|
569
|
+
return self.class::NODE_TYPE
|
570
|
+
end
|
571
|
+
|
572
|
+
# XInvocation
|
573
|
+
def getIntrospection
|
574
|
+
return nil
|
575
|
+
end
|
576
|
+
|
577
|
+
def setValue(name, value)
|
578
|
+
end
|
579
|
+
|
580
|
+
def getValue(name)
|
581
|
+
return nil
|
582
|
+
end
|
583
|
+
|
584
|
+
def hasMethod(name)
|
585
|
+
return false
|
586
|
+
end
|
587
|
+
|
588
|
+
def hasProperty(name)
|
589
|
+
return false
|
590
|
+
end
|
591
|
+
|
592
|
+
def invoke(name, arg1, arg2, arg3)
|
593
|
+
return [false, [0], [nil]]
|
594
|
+
end
|
595
|
+
|
596
|
+
# XPrpertySet
|
597
|
+
def getPropertySetInfo
|
598
|
+
return nil
|
599
|
+
end
|
600
|
+
|
601
|
+
def addPropertyChangeListener(name, listener)
|
602
|
+
end
|
603
|
+
|
604
|
+
def removePropertyChangeListener(name, listener)
|
605
|
+
end
|
606
|
+
|
607
|
+
def addVetoableChangeListener(name, listener)
|
608
|
+
end
|
609
|
+
|
610
|
+
def removeVetoableChangeListener(name, listener)
|
611
|
+
end
|
612
|
+
|
613
|
+
def setPropertyValue(name, value)
|
614
|
+
end
|
615
|
+
|
616
|
+
def getPropertyValue(name)
|
617
|
+
return nil
|
618
|
+
end
|
619
|
+
|
620
|
+
def rename(name)
|
621
|
+
url = @url.join(@url.dirname, name)
|
622
|
+
@storage.rename(@url, url)
|
623
|
+
return url
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
627
|
+
#
|
628
|
+
class ScriptBrowseNode < BaseNode
|
629
|
+
NODE_TYPE = BrowseNodeTypes::SCRIPT
|
630
|
+
|
631
|
+
def initialize(url, storage, name, func_name, description)
|
632
|
+
super(url, storage, name)
|
633
|
+
@func_name = func_name.to_s
|
634
|
+
@description = description.to_s
|
635
|
+
end
|
636
|
+
|
637
|
+
def getPropertyValue(name)
|
638
|
+
case name
|
639
|
+
when "URI"
|
640
|
+
return @url.to_uri(@func_name)
|
641
|
+
when "Description"
|
642
|
+
return @description
|
643
|
+
when "Editable"
|
644
|
+
return false
|
645
|
+
end
|
646
|
+
end
|
647
|
+
end
|
648
|
+
|
649
|
+
# describes file
|
650
|
+
class FileBrowseNode < BaseNode
|
651
|
+
|
652
|
+
def initialize(url, storage, name)
|
653
|
+
super(url, storage, name)
|
654
|
+
end
|
655
|
+
|
656
|
+
def parse_entry(entry)
|
657
|
+
begin
|
658
|
+
return entry["name"], entry["description"]
|
659
|
+
rescue
|
660
|
+
return "", ""
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
def getChildNodes
|
665
|
+
result = []
|
666
|
+
if @storage.exists?(@url)
|
667
|
+
begin
|
668
|
+
file_name = @url.basename
|
669
|
+
descriptions = @storage.descriptions(@url)
|
670
|
+
@storage.get_method_names(@url).each do |name|
|
671
|
+
display_name, description = parse_entry(descriptions[name])
|
672
|
+
display_name = name if display_name.empty?
|
673
|
+
result << ScriptBrowseNode.new(
|
674
|
+
@url, @storage, display_name, name, description)
|
675
|
+
end
|
676
|
+
rescue => e
|
677
|
+
p e.to_s + "\n" + e.backtrace.join("\n") + "\n"
|
678
|
+
end
|
679
|
+
end
|
680
|
+
return result
|
681
|
+
end
|
682
|
+
|
683
|
+
def getPropertyValue(name)
|
684
|
+
ret = false
|
685
|
+
case name
|
686
|
+
when "Renamable", "Deletable"
|
687
|
+
ret = ! @storage.is_readonly?(@url)
|
688
|
+
when "Editable"
|
689
|
+
ret = @url.transient? && ! @storage.is_readonly?(@url)
|
690
|
+
end
|
691
|
+
return ret
|
692
|
+
end
|
693
|
+
|
694
|
+
def invoke(name, args, arg2, arg3)
|
695
|
+
value = false
|
696
|
+
begin
|
697
|
+
case name
|
698
|
+
when "Editable"
|
699
|
+
# push yaml file too if there
|
700
|
+
source = @storage.choose_file
|
701
|
+
@storage.file_copy(source, @url, true) if source
|
702
|
+
when "Renamable"
|
703
|
+
name = args[0]
|
704
|
+
name += RB_EXTENSION unless name.end_with?(RB_EXTENSION)
|
705
|
+
dest = rename(name)
|
706
|
+
update(name, dest)
|
707
|
+
value = self
|
708
|
+
when "Deletable"
|
709
|
+
value = @storage.delete(@url)
|
710
|
+
end
|
711
|
+
rescue => e
|
712
|
+
p %Q!#{e}\n#{e.backtrace.join("\n")}\n!
|
713
|
+
end
|
714
|
+
return [value, [0], [nil]]
|
715
|
+
end
|
716
|
+
end
|
717
|
+
|
718
|
+
# describes directory
|
719
|
+
class DirBrowseNode < BaseNode
|
720
|
+
|
721
|
+
def initialize(url, storage, name)
|
722
|
+
super(url, storage, name)
|
723
|
+
end
|
724
|
+
|
725
|
+
def getChildNodes
|
726
|
+
ret = []
|
727
|
+
begin
|
728
|
+
if @storage.exists?(@url)
|
729
|
+
@storage.dir_contents(@url).sort!.each do |name|
|
730
|
+
url = @url.create(name)
|
731
|
+
if @storage.is_dir?(url)
|
732
|
+
ret << DirBrowseNode.new(url, @storage, url.basename)
|
733
|
+
elsif File::extname(name) == RB_EXTENSION
|
734
|
+
ret << FileBrowseNode.new(url, @storage, url.basename(RB_EXTENSION))
|
735
|
+
end
|
736
|
+
end
|
737
|
+
end
|
738
|
+
rescue => e
|
739
|
+
p e
|
740
|
+
end
|
741
|
+
return ret
|
742
|
+
end
|
743
|
+
|
744
|
+
def getPropertyValue(name)
|
745
|
+
ret = false
|
746
|
+
case name
|
747
|
+
when "Renamable", "Deletable", "Creatable"
|
748
|
+
ret = ! @storage.is_readonly?(@url)
|
749
|
+
end
|
750
|
+
return ret
|
751
|
+
end
|
752
|
+
|
753
|
+
def invoke(name, args, arg2, arg3)
|
754
|
+
value = false
|
755
|
+
begin
|
756
|
+
case name
|
757
|
+
when "Creatable"
|
758
|
+
name = args[0]
|
759
|
+
if name.end_with?("/")
|
760
|
+
name = name[0..-2]
|
761
|
+
url = @url.create(@url.join(name))
|
762
|
+
@storage.dir_create(url)
|
763
|
+
node = DirBrowseNode.new(url, @storage, name)
|
764
|
+
else
|
765
|
+
name += RB_EXTENSION unless name.end_with?(RB_EXTENSION)
|
766
|
+
url = @url.create(@url.join(name))
|
767
|
+
@storage.file_create(@url, name)
|
768
|
+
node = FileBrowseNode.new(url, @storage, url.basename(RB_EXTENSION))
|
769
|
+
end
|
770
|
+
value = node
|
771
|
+
when "Renamable"
|
772
|
+
name = args[0]
|
773
|
+
dest = rename(name)
|
774
|
+
update(name, dest)
|
775
|
+
value = self
|
776
|
+
when "Deletable"
|
777
|
+
value = @storage.delete(@url)
|
778
|
+
end
|
779
|
+
rescue => e
|
780
|
+
p e, e.backtrace.join("\n")
|
781
|
+
end
|
782
|
+
return [value, [0], [nil]]
|
783
|
+
end
|
784
|
+
end
|
785
|
+
|
786
|
+
module PackageManager
|
787
|
+
PACKAGE_REGISTERED = "extensions-ruby.txt"
|
788
|
+
|
789
|
+
def package_manager_init(ctx, context)
|
790
|
+
@context = context
|
791
|
+
@file_path = ctx.getServiceManager.createInstanceWithContext(
|
792
|
+
"com.sun.star.util.PathSubstitution", ctx).substituteVariables(
|
793
|
+
"$(user)/Scripts/#{PACKAGE_REGISTERED}", true)
|
794
|
+
@packages = {}
|
795
|
+
load_registerd
|
796
|
+
end
|
797
|
+
|
798
|
+
def packages
|
799
|
+
packages = {}
|
800
|
+
@packages.each do |name, state|
|
801
|
+
if state
|
802
|
+
if name.find("USER")
|
803
|
+
storage = "user"
|
804
|
+
else
|
805
|
+
storage = @context
|
806
|
+
end
|
807
|
+
if storage == @context
|
808
|
+
url = @url.expand(name)
|
809
|
+
packages[url] = File::basename(url)
|
810
|
+
end
|
811
|
+
end
|
812
|
+
end
|
813
|
+
return packages
|
814
|
+
end
|
815
|
+
|
816
|
+
# XNameContainer
|
817
|
+
def hasByName(name)
|
818
|
+
return @packages.has_key?(name)
|
819
|
+
end
|
820
|
+
|
821
|
+
def insertByName(name, value)
|
822
|
+
@packages[name] = value.getName
|
823
|
+
store_registered
|
824
|
+
end
|
825
|
+
|
826
|
+
def removeByName(name)
|
827
|
+
@packages.delete(name)
|
828
|
+
store_registered
|
829
|
+
end
|
830
|
+
|
831
|
+
def replaceByName(name, value)
|
832
|
+
@packages[name] = value.getName
|
833
|
+
end
|
834
|
+
|
835
|
+
def getByName(name)
|
836
|
+
return nil
|
837
|
+
end
|
838
|
+
|
839
|
+
def getElementNames
|
840
|
+
return []
|
841
|
+
end
|
842
|
+
|
843
|
+
def getElementType
|
844
|
+
return Uno.get_type("void")
|
845
|
+
end
|
846
|
+
|
847
|
+
def hasElements
|
848
|
+
return false
|
849
|
+
end
|
850
|
+
|
851
|
+
private
|
852
|
+
|
853
|
+
def has_scripts?(url)
|
854
|
+
@storage.dir_contents(url).each do |name|
|
855
|
+
if @storage.is_dir?(name)
|
856
|
+
return true if has_scripts?(name)
|
857
|
+
elsif name.end_with(RB_EXTENSION)
|
858
|
+
return true
|
859
|
+
end
|
860
|
+
end
|
861
|
+
return false
|
862
|
+
end
|
863
|
+
|
864
|
+
def load_registerd
|
865
|
+
if File.exist?(@file_path)
|
866
|
+
@packages.clear
|
867
|
+
lines = IO.readlines(@file_path)
|
868
|
+
lines.each do |name|
|
869
|
+
name.strip!
|
870
|
+
unless name.empty?
|
871
|
+
url = @url.expand(name)
|
872
|
+
@packages[name] = has_scripts?(url)
|
873
|
+
end
|
874
|
+
end
|
875
|
+
end
|
876
|
+
end
|
877
|
+
|
878
|
+
def store_registered
|
879
|
+
if File.writable?(File::dirname(@file_path))
|
880
|
+
open(@file_path, "w") do |f|
|
881
|
+
f.write(@packages.keys.join("\n"))
|
882
|
+
end
|
883
|
+
end
|
884
|
+
end
|
885
|
+
end
|
886
|
+
|
887
|
+
# package node is read-only
|
888
|
+
class PackageBrowseNode < DirBrowseNode
|
889
|
+
def initialize(url, storage, name, manager)
|
890
|
+
super(url, storage, name)
|
891
|
+
@manager = manager
|
892
|
+
end
|
893
|
+
|
894
|
+
def getChildNodes
|
895
|
+
ret = []
|
896
|
+
@manager.packages.each do |url, name|
|
897
|
+
ret << DirBrowseNode.new(@url.create(url), @storage, name)
|
898
|
+
end
|
899
|
+
return ret
|
900
|
+
end
|
901
|
+
|
902
|
+
def getPropertyValue(name)
|
903
|
+
return false
|
904
|
+
end
|
905
|
+
|
906
|
+
def invoke(name, args, arg2, arg3)
|
907
|
+
return [false, [0], [nil]]
|
908
|
+
end
|
909
|
+
end
|
910
|
+
|
911
|
+
|
912
|
+
# Executes script.
|
913
|
+
class RubyScript
|
914
|
+
include Uno::UnoBase
|
915
|
+
include Runo::Com::Sun::Star::Script::Provider::XScript
|
916
|
+
|
917
|
+
def initialize(capsule, file_name, func_name)
|
918
|
+
@capsule = capsule
|
919
|
+
@file_name = file_name
|
920
|
+
@func_name = func_name
|
921
|
+
end
|
922
|
+
|
923
|
+
def invoke(args, param_index, out_params)
|
924
|
+
ret = nil
|
925
|
+
begin
|
926
|
+
m = @capsule.method(@func_name)
|
927
|
+
if m
|
928
|
+
ret = m.call(*args)
|
929
|
+
end
|
930
|
+
rescue Uno::UnoError => e
|
931
|
+
trace = e.backtrace
|
932
|
+
trace.unshift(trace[0].gsub(
|
933
|
+
/`[^']*'/, "\`#{e.uno_method}'")) if e.respond_to? :uno_method
|
934
|
+
trace.pop(2)
|
935
|
+
e.uno_exception.Message += "\nError during invoking method: \n" +
|
936
|
+
"Script: #{@func_name}\n" +
|
937
|
+
"File: #{@file_name}\nType: #{e.type_name}" + "\n\n" +
|
938
|
+
%Q!#{e.uno_exception}\n#{trace.join("\n")}\n!
|
939
|
+
raise
|
940
|
+
rescue => e
|
941
|
+
ex = RuntimeException.new(
|
942
|
+
%Q!\n#{e}\n#{e.backtrace.join("\n")}\n!, self)
|
943
|
+
raise Uno::UnoError, ex
|
944
|
+
end
|
945
|
+
return [ret, [0], [nil]]
|
946
|
+
end
|
947
|
+
end
|
948
|
+
|
949
|
+
# The script provider for Ruby.
|
950
|
+
class ScriptProviderImpl < BaseNode
|
951
|
+
include Uno::UnoComponentBase
|
952
|
+
include Runo::Com::Sun::Star::Script::Provider::XScriptProvider
|
953
|
+
include Runo::Com::Sun::Star::Lang::XServiceInfo
|
954
|
+
include Runo::Com::Sun::Star::Container::XNameContainer
|
955
|
+
|
956
|
+
IMPLE_NAME = "mytools.script.provider.ScriptProviderForRuby"
|
957
|
+
SERVICE_NAMES = [
|
958
|
+
"com.sun.star.script.provider.ScriptProviderForRuby",
|
959
|
+
"com.sun.star.script.provider.LanguageScriptProvider"]
|
960
|
+
NODE_TYPE = BrowseNodeTypes::ROOT
|
961
|
+
|
962
|
+
def initialize(ctx, args)
|
963
|
+
@name = RubyScriptProvider::LANGUAGE
|
964
|
+
doc = nil
|
965
|
+
inv = nil
|
966
|
+
is_package = false
|
967
|
+
context = ""
|
968
|
+
|
969
|
+
if args[0].kind_of?(String)
|
970
|
+
context = args[0]
|
971
|
+
is_package = context.end_with?(":uno_packages")
|
972
|
+
doc = RubyScriptProvider.get_document_model(ctx, context) if
|
973
|
+
context.start_with?(DOC_PROTOCOL)
|
974
|
+
else
|
975
|
+
inv = args[0]
|
976
|
+
doc = inv.ScriptContainer
|
977
|
+
context = RubyScriptProvider.get_context(ctx, doc)
|
978
|
+
end
|
979
|
+
@script_context = ScriptContext.new(ctx, doc, inv)
|
980
|
+
@url = URL.new(ctx, context, nil)
|
981
|
+
@storage = Storage.new(ctx)
|
982
|
+
if is_package
|
983
|
+
self.extend PackageManager
|
984
|
+
package_manager_init(ctx, context)
|
985
|
+
@node = PackageBrowseNode.new(@url, @storage, LANGUAGE, self)
|
986
|
+
else
|
987
|
+
@node = DirBrowseNode.new(@url, @storage, LANGUAGE)
|
988
|
+
end
|
989
|
+
end
|
990
|
+
|
991
|
+
def getChildNodes
|
992
|
+
@node.getChildNodes
|
993
|
+
end
|
994
|
+
|
995
|
+
def getScript(uri)
|
996
|
+
begin
|
997
|
+
script = RubyScripts.get_script(@storage, @script_context, @url, uri)
|
998
|
+
return script
|
999
|
+
rescue StandardError, ScriptError => e
|
1000
|
+
ex = ScriptFrameworkErrorException.new(
|
1001
|
+
%Q!\n#{e.message}\n#{e.backtrace.join("\n")}\n!,
|
1002
|
+
nil, uri, LANGUAGE, 0)
|
1003
|
+
raise Uno::UnoError, ex
|
1004
|
+
end
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
def invoke(name, params, arg2, arg3)
|
1008
|
+
result = [false, [0], [nil]]
|
1009
|
+
case name
|
1010
|
+
when "Creatable"
|
1011
|
+
result = @node.invoke(name, params, arg2, arg3)
|
1012
|
+
end
|
1013
|
+
return result
|
1014
|
+
end
|
1015
|
+
|
1016
|
+
def getPropertyValue(name)
|
1017
|
+
case name
|
1018
|
+
when "Creatable"
|
1019
|
+
return @node.editable
|
1020
|
+
end
|
1021
|
+
return false
|
1022
|
+
end
|
1023
|
+
end
|
1024
|
+
end
|
1025
|
+
|