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