xref: /trunk/main/scripting/source/pyprov/pythonscript.py (revision d2cc3a1ab9e89fb0e1752bc9ca5a424202cdf3ce)
1# *************************************************************
2#
3#  Licensed to the Apache Software Foundation (ASF) under one
4#  or more contributor license agreements.  See the NOTICE file
5#  distributed with this work for additional information
6#  regarding copyright ownership.  The ASF licenses this file
7#  to you under the Apache License, Version 2.0 (the
8#  "License"); you may not use this file except in compliance
9#  with the License.  You may obtain a copy of the License at
10#
11#    http://www.apache.org/licenses/LICENSE-2.0
12#
13#  Unless required by applicable law or agreed to in writing,
14#  software distributed under the License is distributed on an
15#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16#  KIND, either express or implied.  See the License for the
17#  specific language governing permissions and limitations
18#  under the License.
19#
20# *************************************************************
21
22# XScript implementation for python
23import uno
24import unohelper
25import sys
26import os
27import imp
28import time
29import ast
30
31try:
32    unicode
33except NameError:
34    unicode = str
35
36class LogLevel:
37    NONE = 0   # production level
38    ERROR = 1  # for script developers
39    DEBUG = 2  # for script framework developers
40
41PYSCRIPT_LOG_ENV = "PYSCRIPT_LOG_LEVEL"
42PYSCRIPT_LOG_STDOUT_ENV = "PYSCRIPT_LOG_STDOUT"
43
44# Configuration ----------------------------------------------------
45LogLevel.use = LogLevel.NONE
46if os.environ.get(PYSCRIPT_LOG_ENV) == "ERROR":
47    LogLevel.use = LogLevel.ERROR
48elif os.environ.get(PYSCRIPT_LOG_ENV) == "DEBUG":
49    LogLevel.use = LogLevel.DEBUG
50
51# True, writes to stdout (difficult on windows)
52# False, writes to user/Scripts/python/log.txt
53LOG_STDOUT = os.environ.get(PYSCRIPT_LOG_STDOUT_ENV, "1") != "0"
54
55#-------------------------------------------------------------------
56
57def encfile(uni):
58    if sys.version_info[0] > 2:
59        return uni
60    else:
61        return uni.encode( sys.getfilesystemencoding())
62
63def lastException2String():
64    (excType,excInstance,excTraceback) = sys.exc_info()
65    ret = str(excType) + ": "+str(excInstance) + "\n" + \
66          uno._uno_extract_printable_stacktrace( excTraceback )
67    return ret
68
69def logLevel2String( level ):
70    ret = " NONE"
71    if level == LogLevel.ERROR:
72        ret = "ERROR"
73    elif level >= LogLevel.DEBUG:
74        ret = "DEBUG"
75    return ret
76
77def getLogTarget():
78    ret = sys.stdout
79    if not LOG_STDOUT:
80        try:
81            pathSubst = uno.getComponentContext().ServiceManager.createInstance(
82                "com.sun.star.util.PathSubstitution" )
83            userInstallation =  pathSubst.getSubstituteVariableValue( "user" )
84            if len( userInstallation ) > 0:
85                systemPath = uno.fileUrlToSystemPath( userInstallation + "/Scripts/python/log.txt" )
86                ret = open( systemPath , "a" )
87        except Exception as e:
88            print("Exception during creation of pythonscript logfile: "+ lastException2String() + "\n, delagating log to stdout\n")
89    return ret
90
91class Logger(LogLevel):
92    def __init__(self , target ):
93        self.target = target
94
95    def isDebugLevel( self ):
96        return self.use >= self.DEBUG
97
98    def debug( self, msg ):
99        if self.isDebugLevel():
100            self.log( self.DEBUG, msg )
101
102    def isErrorLevel( self ):
103        return self.use >= self.ERROR
104
105    def error( self, msg ):
106        if self.isErrorLevel():
107            self.log( self.ERROR, msg )
108
109    def log( self, level, msg ):
110        if self.use >= level:
111            try:
112                self.target.write(
113                    time.asctime() +
114                    " [" +
115                    logLevel2String( level ) +
116                    "] " +
117                    encfile(msg) +
118                    "\n" )
119                self.target.flush()
120            except Exception as e:
121                print("Error during writing to stdout: " +lastException2String() + "\n")
122
123log = Logger( getLogTarget() )
124
125log.debug( "pythonscript loading" )
126
127#from com.sun.star.lang import typeOfXServiceInfo, typeOfXTypeProvider
128from com.sun.star.uno import RuntimeException
129from com.sun.star.lang import XServiceInfo
130from com.sun.star.io import IOException, XInputStream
131from com.sun.star.ucb import CommandAbortedException, XCommandEnvironment, XProgressHandler, Command
132from com.sun.star.task import XInteractionHandler
133from com.sun.star.beans import XPropertySet, Property
134from com.sun.star.container import XNameContainer
135from com.sun.star.xml.sax import XDocumentHandler, InputSource
136from com.sun.star.uno import Exception as UnoException
137from com.sun.star.script import XInvocation
138from com.sun.star.awt import XActionListener
139
140from com.sun.star.script.provider import XScriptProvider, XScript, XScriptContext, ScriptFrameworkErrorException
141from com.sun.star.script.browse import XBrowseNode
142from com.sun.star.script.browse.BrowseNodeTypes import SCRIPT, CONTAINER, ROOT
143from com.sun.star.util import XModifyListener
144
145LANGUAGENAME = "Python"
146GLOBAL_SCRIPTCONTEXT_NAME = "XSCRIPTCONTEXT"
147CALLABLE_CONTAINER_NAME =  "g_exportedScripts"
148
149# pythonloader looks for a static g_ImplementationHelper variable
150g_ImplementationHelper = unohelper.ImplementationHelper()
151g_implName = "org.openoffice.pyuno.LanguageScriptProviderFor"+LANGUAGENAME
152
153
154
155BLOCK_SIZE = 65536
156def readTextFromStream( inputStream ):
157    # read the file
158    code = uno.ByteSequence( b"" )
159    while True:
160        read,out = inputStream.readBytes( None , BLOCK_SIZE )
161        code = code + out
162        if read < BLOCK_SIZE:
163            break
164    if sys.version_info[0] > 2:
165        return str( code.value, 'utf-8' )
166    else:
167        return code.value
168
169def toIniName( str ):
170    # TODO: what is the official way to get to know whether i am on the windows platform ?
171    if( hasattr(sys , "dllhandle") ):
172        return str + ".ini"
173    return str + "rc"
174
175class EmptyInputStream( unohelper.Base, XInputStream ):
176    def __init__( self ):
177        pass
178
179    def closeInput(self):
180        pass
181
182    def readBytes( self, seq, n ):
183        return 0, ""
184
185    def readSomeBytes( self, seq, n ):
186        return 0, ""
187
188    def skipBytes( self, n ):
189        pass
190
191    def available( self ):
192        return 0
193
194class BytesInputStream( unohelper.Base, XInputStream ):
195    def __init__( self, bytes ):
196        self.bytes = bytes
197        self.position = 0
198
199    def closeInput(self):
200        pass
201
202    def readBytes( self, seq, n ):
203        size = self.available()
204        if n < size:
205            size = n
206        curr = self.position
207        self.position += size
208        return size, uno.ByteSequence( self.bytes[curr:curr+size] )
209
210    def readSomeBytes( self, seq, n ):
211        return self.readBytes( seq, n )
212
213    def skipBytes( self, n ):
214        size = self.available()
215        if n < size:
216            size = n
217        self.position += size
218
219    def available( self ):
220        return len( self.bytes ) - self.position
221
222""" definition: storageURI is the system dependent, absolute file url, where the script is stored on disk
223                scriptURI is the system independent uri
224"""
225class MyUriHelper:
226
227    def __init__( self, ctx, location ):
228        self.s_UriMap = \
229        { "share" : "vnd.sun.star.expand:${$OOO_BASE_DIR/program/" +  toIniName( "bootstrap") + "::BaseInstallation}/share/Scripts/python" , \
230          "share:uno_packages" : "vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE/uno_packages", \
231          "user" : "vnd.sun.star.expand:${$OOO_BASE_DIR/program/" + toIniName( "bootstrap") + "::UserInstallation}/user/Scripts/python" , \
232          "user:uno_packages" : "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/uno_packages" }
233        self.m_uriRefFac = ctx.ServiceManager.createInstanceWithContext("com.sun.star.uri.UriReferenceFactory",ctx)
234        if location.startswith( "vnd.sun.star.tdoc" ):
235            self.m_baseUri = location + "/Scripts/python"
236            self.m_scriptUriLocation = "document"
237        else:
238            self.m_baseUri = expandUri( self.s_UriMap[location] )
239            self.m_scriptUriLocation = location
240        log.debug( "initialized urihelper with baseUri="+self.m_baseUri + ",m_scriptUriLocation="+self.m_scriptUriLocation )
241
242    def getRootStorageURI( self ):
243        return self.m_baseUri
244
245    def getStorageURI( self, scriptURI ):
246        return self.scriptURI2StorageUri(scriptURI)
247
248    def getScriptURI( self, storageURI ):
249        return self.storageURI2ScriptUri(storageURI)
250
251    def storageURI2ScriptUri( self, storageURI ):
252        if not storageURI.startswith( self.m_baseUri ):
253            message = "pythonscript: storage uri '" + storageURI + "' not in base uri '" + self.m_baseUri + "'"
254            log.debug( message )
255            raise RuntimeException( message )
256
257        ret = "vnd.sun.star.script:" + \
258              storageURI[len(self.m_baseUri)+1:].replace("/","|") + \
259              "?language=" + LANGUAGENAME + "&location=" + self.m_scriptUriLocation
260        log.debug( "converting storageURI="+storageURI + " to scriptURI=" + ret )
261        return ret
262
263    def scriptURI2StorageUri( self, scriptURI ):
264        try:
265            myUri = self.m_uriRefFac.parse(scriptURI)
266            ret = self.m_baseUri + "/" + myUri.getName().replace( "|", "/" )
267            log.debug( "converting scriptURI="+scriptURI + " to storageURI=" + ret )
268            return ret
269        except UnoException as e:
270            log.error( "error during converting scriptURI="+scriptURI + ": " + e.Message)
271            raise RuntimeException( "pythonscript:scriptURI2StorageUri: " +e.getMessage(), None )
272        except Exception as e:
273            log.error( "error during converting scriptURI="+scriptURI + ": " + str(e))
274            raise RuntimeException( "pythonscript:scriptURI2StorageUri: " + str(e), None )
275
276
277class ModuleEntry:
278    def __init__( self, lastRead, module ):
279        self.lastRead = lastRead
280        self.module = module
281
282def hasChanged( oldDate, newDate ):
283    return newDate.Year > oldDate.Year or \
284           newDate.Month > oldDate.Month or \
285           newDate.Day > oldDate.Day or \
286           newDate.Hours > oldDate.Hours or \
287           newDate.Minutes > oldDate.Minutes or \
288           newDate.Seconds > oldDate.Seconds or \
289           newDate.HundredthSeconds > oldDate.HundredthSeconds
290
291def ensureSourceState( code ):
292    if not code.endswith( "\n" ):
293        code = code + "\n"
294    code = code.replace( "\r", "" )
295    return code
296
297
298def checkForPythonPathBesideScript( url ):
299    if url.startswith( "file:" ):
300        path = unohelper.fileUrlToSystemPath( url+"/pythonpath.zip" );
301        log.log( LogLevel.DEBUG,  "checking for existence of " + path )
302        if 1 == os.access( encfile(path), os.F_OK) and not path in sys.path:
303            log.log( LogLevel.DEBUG, "adding " + path + " to sys.path" )
304            sys.path.append( path )
305
306        path = unohelper.fileUrlToSystemPath( url+"/pythonpath" );
307        log.log( LogLevel.DEBUG,  "checking for existence of " + path )
308        if 1 == os.access( encfile(path), os.F_OK) and not path in sys.path:
309            log.log( LogLevel.DEBUG, "adding " + path + " to sys.path" )
310            sys.path.append( path )
311
312
313class ScriptContext(unohelper.Base):
314    def __init__( self, ctx, doc, inv ):
315        self.ctx = ctx
316        self.doc = doc
317        self.inv = inv
318
319   # XScriptContext
320    def getDocument(self):
321        if self.doc:
322            return self.doc
323        return self.getDesktop().getCurrentComponent()
324
325    def getDesktop(self):
326        return self.ctx.ServiceManager.createInstanceWithContext(
327            "com.sun.star.frame.Desktop", self.ctx )
328
329    def getComponentContext(self):
330        return self.ctx
331
332    def getInvocationContext(self):
333        return self.inv
334
335#----------------------------------
336# Global Module Administration
337# does not fit together with script
338# engine lifetime management
339#----------------------------------
340#g_scriptContext = ScriptContext( uno.getComponentContext(), None )
341#g_modules = {}
342#def getModuleByUrl( url, sfa ):
343#    entry =  g_modules.get(url)
344#    load = True
345#    lastRead = sfa.getDateTimeModified( url )
346#    if entry:
347#        if hasChanged( entry.lastRead, lastRead ):
348#            log.debug("file " + url + " has changed, reloading")
349#        else:
350#            load = False
351#
352#    if load:
353#        log.debug( "opening >" + url + "<" )
354#
355#        code = readTextFromStream( sfa.openFileRead( url ) )
356
357        # execute the module
358#        entry = ModuleEntry( lastRead, imp.new_module("ooo_script_framework") )
359#        entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = g_scriptContext
360#        entry.module.__file__ = url
361#        exec code in entry.module.__dict__
362#        g_modules[ url ] = entry
363#        log.debug( "mapped " + url + " to " + str( entry.module ) )
364#    return entry.module
365
366class ProviderContext:
367    def __init__( self, storageType, sfa, uriHelper, scriptContext ):
368        self.storageType = storageType
369        self.sfa = sfa
370        self.uriHelper = uriHelper
371        self.scriptContext = scriptContext
372        self.modules = {}
373        self.rootUrl = None
374        self.mapPackageName2Path = None
375
376    def getTransientPartFromUrl( self, url ):
377        rest = url.replace( self.rootUrl , "",1 ).replace( "/","",1)
378        return rest[0:rest.find("/")]
379
380    def getPackageNameFromUrl( self, url ):
381        rest = url.replace( self.rootUrl , "",1 ).replace( "/","",1)
382        start = rest.find("/") +1
383        return rest[start:rest.find("/",start)]
384
385
386    def removePackageByUrl( self, url ):
387        items = list(self.mapPackageName2Path.items())
388        for i in items:
389            if url in i[1].pathes:
390                self.mapPackageName2Path.pop(i[0])
391                break
392
393    def addPackageByUrl( self, url ):
394        packageName = self.getPackageNameFromUrl( url )
395        transientPart = self.getTransientPartFromUrl( url )
396        log.debug( "addPackageByUrl : " + packageName + ", " + transientPart + "("+url+")" + ", rootUrl="+self.rootUrl )
397        if packageName in self.mapPackageName2Path:
398            package = self.mapPackageName2Path[ packageName ]
399            package.pathes = package.pathes + (url, )
400        else:
401            package = Package( (url,), transientPart)
402            self.mapPackageName2Path[ packageName ] = package
403
404    def isUrlInPackage( self, url ):
405        values = list(self.mapPackageName2Path.values())
406        for i in values:
407#           print "checking " + url + " in " + str(i.pathes)
408            if url in i.pathes:
409                return True
410#        print "false"
411        return False
412
413    def setPackageAttributes( self, mapPackageName2Path, rootUrl ):
414        self.mapPackageName2Path = mapPackageName2Path
415        self.rootUrl = rootUrl
416
417    def getPersistentUrlFromStorageUrl( self, url ):
418        # package name is the second directory
419        ret = url
420        if self.rootUrl:
421            pos = len( self.rootUrl) +1
422            ret = url[0:pos]+url[url.find("/",pos)+1:len(url)]
423        log.debug( "getPersistentUrlFromStorageUrl " + url +  " -> "+ ret)
424        return ret
425
426    def getStorageUrlFromPersistentUrl( self, url):
427        ret = url
428        if self.rootUrl:
429            pos = len(self.rootUrl)+1
430            packageName = url[pos:url.find("/",pos+1)]
431            package = self.mapPackageName2Path[ packageName ]
432            ret = url[0:pos]+ package.transientPathElement + "/" + url[pos:len(url)]
433        log.debug( "getStorageUrlFromPersistentUrl " + url + " -> "+ ret)
434        return ret
435
436    def getFuncsByUrl( self, url ):
437        src = readTextFromStream( self.sfa.openFileRead( url ) )
438        checkForPythonPathBesideScript( url[0:url.rfind('/')] )
439        src = ensureSourceState( src )
440
441        allFuncs = []
442        g_exportedScripts = []
443
444        a = ast.parse(src, url)
445
446        if isinstance(a, ast.Module):
447            for node in a.body:
448                if isinstance(node, ast.FunctionDef):
449                    allFuncs.append(node.name)
450                elif isinstance(node, ast.Assign):
451                    is_exported = False
452                    for subnode in node.targets:
453                        if isinstance(subnode, ast.Name) and \
454                            subnode.id == "g_exportedScripts":
455                            is_exported = True
456                            break
457                    if is_exported:
458                        value_node = node.value
459                        if isinstance(value_node, ast.List) or \
460                            isinstance(value_node, ast.Tuple):
461                            for elt in value_node.elts:
462                                if isinstance(elt, ast.Str):
463                                    g_exportedScripts.append(elt.s)
464                                elif isinstance(elt, ast.Name):
465                                    g_exportedScripts.append(elt.id)
466                        elif isinstance(value_node, ast.Str):
467                            g_exportedScripts.append(value_node.s)
468                        elif isinstance(value_node, ast.Name):
469                            g_exportedScripts.append(value_node.id)
470                        return g_exportedScripts
471        return allFuncs
472
473    def getModuleByUrl( self, url ):
474        entry =  self.modules.get(url)
475        load = True
476        lastRead = self.sfa.getDateTimeModified( url )
477        if entry:
478            if hasChanged( entry.lastRead, lastRead ):
479                log.debug( "file " + url + " has changed, reloading" )
480            else:
481                load = False
482
483        if load:
484            log.debug( "opening >" + url + "<" )
485
486            src = readTextFromStream( self.sfa.openFileRead( url ) )
487            checkForPythonPathBesideScript( url[0:url.rfind('/')] )
488            src = ensureSourceState( src )
489
490            # execute the module
491            entry = ModuleEntry( lastRead, imp.new_module("ooo_script_framework") )
492            entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.scriptContext
493
494            code = None
495            if url.startswith( "file:" ):
496                code = compile( src, encfile(uno.fileUrlToSystemPath( url ) ), "exec" )
497            else:
498                code = compile( src, url, "exec" )
499            exec(code, entry.module.__dict__)
500            entry.module.__file__ = url
501            self.modules[ url ] = entry
502            log.debug( "mapped " + url + " to " + str( entry.module ) )
503        return  entry.module
504
505def createEditorDialog( ctx ):
506    smgr = ctx.ServiceManager
507
508    dialogModel = smgr.createInstanceWithContext(
509        "com.sun.star.awt.UnoControlDialogModel", ctx)
510    dialogModel.PositionX = 105
511    dialogModel.PositionY = 117
512    dialogModel.Width = 240
513    dialogModel.Height = 320
514    dialogModel.Closeable = True
515    dialogModel.Moveable = True
516    dialogModel.Title = "Python Macro Editor"
517
518    runButtonModel = dialogModel.createInstance(
519        "com.sun.star.awt.UnoControlButtonModel" )
520    runButtonModel.PositionX = 57
521    runButtonModel.PositionY = 300
522    runButtonModel.Width = 40
523    runButtonModel.Height = 14
524    runButtonModel.TabIndex = 0
525    runButtonModel.Label = "Run"
526
527    saveButtonModel = dialogModel.createInstance(
528        "com.sun.star.awt.UnoControlButtonModel" )
529    saveButtonModel.PositionX = 100
530    saveButtonModel.PositionY = 300
531    saveButtonModel.Width = 40
532    saveButtonModel.Height = 14
533    saveButtonModel.TabIndex = 1
534    saveButtonModel.Label = "Save"
535
536    closeButtonModel = dialogModel.createInstance(
537        "com.sun.star.awt.UnoControlButtonModel" )
538    closeButtonModel.PositionX = 143
539    closeButtonModel.PositionY = 300
540    closeButtonModel.Width = 40
541    closeButtonModel.Height = 14
542    closeButtonModel.TabIndex = 2
543    closeButtonModel.PushButtonType = 2  # CANCEL
544    closeButtonModel.Label = "Close"
545
546    textFieldModel = dialogModel.createInstance(
547        "com.sun.star.awt.UnoControlEditModel" )
548    textFieldModel.PositionX = 6
549    textFieldModel.PositionY = 6
550    textFieldModel.Width = 228
551    textFieldModel.Height = 288
552    textFieldModel.TabIndex = 3
553    textFieldModel.HScroll = True
554    textFieldModel.VScroll = True
555    textFieldModel.MultiLine = True
556
557    dialogModel.insertByName( "RunButton", runButtonModel )
558    dialogModel.insertByName( "SaveButton", saveButtonModel )
559    dialogModel.insertByName( "CloseButton", closeButtonModel )
560    dialogModel.insertByName( "EditorTextField", textFieldModel )
561
562    # create the dialog control and set the model
563    controlContainer = smgr.createInstanceWithContext(
564        "com.sun.star.awt.UnoControlDialog", ctx);
565    controlContainer.setModel(dialogModel);
566
567    # create a peer
568    toolkit = smgr.createInstanceWithContext(
569        "com.sun.star.awt.ExtToolkit", ctx);
570
571    controlContainer.setVisible(False);
572    controlContainer.createPeer(toolkit, None);
573
574    return controlContainer
575
576
577#--------------------------------------------------
578def isScript( candidate ):
579    ret = False
580    if isinstance( candidate, type(isScript) ):
581        ret = True
582    return ret
583
584#-------------------------------------------------------
585class ScriptBrowseNode( unohelper.Base, XBrowseNode, XPropertySet, XInvocation, XActionListener ):
586    def __init__( self, provCtx, parent, fileName, funcName ):
587        self.parent = parent
588        self.fileName = fileName
589        self.funcName = funcName
590        self.provCtx = provCtx
591
592    def uri( self ):
593        return self.parent.uri()
594
595    def getName( self ):
596        return self.funcName
597
598    def getChildNodes(self):
599        return ()
600
601    def hasChildNodes(self):
602        return False
603
604    def getType( self):
605        return SCRIPT
606
607    def getPropertyValue( self, name ):
608        ret = None
609        try:
610            if name == "URI":
611                ret = self.provCtx.uriHelper.getScriptURI(
612                    self.provCtx.getPersistentUrlFromStorageUrl( self.uri() + "$" + self.funcName ) )
613            elif name == "Editable":
614                ret = not self.provCtx.sfa.isReadOnly( self.uri() )
615
616            log.debug( "ScriptBrowseNode.getPropertyValue called for " + name + ", returning " + str(ret) )
617        except Exception as e:
618            log.error( "ScriptBrowseNode.getPropertyValue error " + lastException2String())
619            raise
620
621        return ret
622    def setPropertyValue( self, name, value ):
623        log.debug( "ScriptBrowseNode.setPropertyValue called " + name + "=" +str(value ) )
624    def getPropertySetInfo( self ):
625        log.debug( "ScriptBrowseNode.getPropertySetInfo called "  )
626        return None
627
628    def getIntrospection( self ):
629        return None
630
631    def invoke( self, name, params, outparamindex, outparams ):
632        if name == "Editable":
633            ctx = self.provCtx.scriptContext.getComponentContext()
634
635            self.editor = createEditorDialog( ctx )
636
637            code = readTextFromStream(self.provCtx.sfa.openFileRead(self.uri()))
638            code = ensureSourceState( code )
639            self.editor.getControl("EditorTextField").setText(code)
640
641            self.editor.getControl("RunButton").setActionCommand("Run")
642            self.editor.getControl("RunButton").addActionListener(self)
643            self.editor.getControl("SaveButton").setActionCommand("Save")
644            self.editor.getControl("SaveButton").addActionListener(self)
645
646            self.editor.execute()
647
648        return None, (), ()
649
650    def actionPerformed( self, event ):
651        try:
652            if event.ActionCommand == "Run":
653                code = self.editor.getControl("EditorTextField").getText()
654                code = ensureSourceState( code )
655                mod = imp.new_module("ooo_script_framework")
656                mod.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.provCtx.scriptContext
657                exec(code, mod.__dict__)
658                values = mod.__dict__.get( CALLABLE_CONTAINER_NAME , None )
659                if not values:
660                    values = list(mod.__dict__.values())
661
662                for i in values:
663                    if isScript( i ):
664                        i()
665                        break
666
667            elif event.ActionCommand == "Save":
668                toWrite = uno.ByteSequence(
669                    self.editor.getControl("EditorTextField").getText().encode("utf-8"))
670                copyUrl = self.uri() + ".orig"
671                self.provCtx.sfa.move( self.uri(), copyUrl )
672                log.debug( "Saving Python macro to URI " + self.uri() )
673                self.provCtx.sfa.writeFile( self.uri(), BytesInputStream( toWrite.value ) )
674                self.provCtx.sfa.kill( copyUrl )
675        except Exception as e:
676            # TODO: add an error box here !
677            log.error( lastException2String() )
678
679
680    def setValue( self, name, value ):
681        return None
682
683    def getValue( self, name ):
684        return None
685
686    def hasMethod( self, name ):
687        return False
688
689    def hasProperty( self, name ):
690        return False
691
692
693#-------------------------------------------------------
694class FileBrowseNode( unohelper.Base, XBrowseNode, XPropertySet, XInvocation, XActionListener ):
695    def __init__( self, provCtx, parent, name ):
696        self.provCtx = provCtx
697        self.parent = parent
698        self.name = name
699        self.funcnames = None
700
701    def uri( self ):
702        return self.parent.rootUrl + "/" + self.name + ".py"
703
704    def getName( self ):
705        return self.name
706
707    def getChildNodes(self):
708        ret = ()
709        try:
710            self.funcnames = self.provCtx.getFuncsByUrl( self.uri() )
711
712            scriptNodeList = []
713            for i in self.funcnames:
714                scriptNodeList.append(
715                    ScriptBrowseNode(
716                    self.provCtx, self, self.name, i ))
717            ret = tuple( scriptNodeList )
718        except Exception as e:
719            text = lastException2String()
720            log.error( "FileBrowseNode.getChildNodes error while evaluating " + self.uri() + ":" + text )
721            raise
722        return ret
723
724    def hasChildNodes(self):
725        try:
726            return len(self.getChildNodes()) > 0
727        except Exception as e:
728            return False
729
730    def getType( self):
731        return CONTAINER
732
733    # XPropertySet
734
735    def getPropertyValue( self, name ):
736        ret = None
737        try:
738            if name == "Editable":
739                ret = not self.provCtx.sfa.isReadOnly( self.uri() )
740            elif name == "Deletable":
741                ret = not self.provCtx.sfa.isReadOnly( self.uri() )
742            elif name == "Renamable":
743                ret = not self.provCtx.sfa.isReadOnly( self.uri() )
744
745            log.debug( "FileBrowseNode.getPropertyValue called for " + name + ", returning " + str(ret) )
746        except Exception as e:
747            log.error( "FileBrowseNode.getPropertyValue error " + lastException2String())
748            raise
749
750        return ret
751
752    def setPropertyValue( self, name, value ):
753        log.debug( "FileBrowseNode.setPropertyValue called " + name + "=" +str(value ) )
754
755    def getPropertySetInfo( self ):
756        log.debug( "FileBrowseNode.getPropertySetInfo called "  )
757        return None
758
759    # XInvocation
760
761    def getIntrospection( self ):
762        log.debug( "FileBrowseNode.getIntrospection() called" )
763        return None
764
765    def invoke( self, name, params, outparamindex, outparams ):
766        log.debug("FileBrowseNode.invoke called for " + name + "," + str(params) + "," + str(outparamindex) + "," + str(outparams))
767        try:
768            if name == "Editable":
769                ctx = self.provCtx.scriptContext.getComponentContext()
770
771                self.editor = createEditorDialog( ctx )
772
773                code = readTextFromStream(self.provCtx.sfa.openFileRead(self.uri()))
774                code = ensureSourceState( code )
775                self.editor.getControl("EditorTextField").setText(code)
776
777                self.editor.getControl("RunButton").setActionCommand("Run")
778                self.editor.getControl("RunButton").addActionListener(self)
779                self.editor.getControl("SaveButton").setActionCommand("Save")
780                self.editor.getControl("SaveButton").addActionListener(self)
781
782                self.editor.execute()
783            elif name == "Deletable":
784                self.provCtx.sfa.kill( self.uri() )
785                return True, (), ()
786            elif name == "Renamable":
787                if params is None or not params:
788                    raise IllegalArgumentException( "invoke with Creatable needs the name in params" )
789                newUri = self.parent.rootUrl + "/" + params[0] + ".py"
790                self.provCtx.sfa.move( self.uri(), newUri )
791                self.name = params[0]
792                return self, (), ()
793        except Exception as e:
794            log.error( "FileBrowseNode.invoke error " + lastException2String() )
795            raise
796        return None, (), ()
797
798    def setValue( self, name, value ):
799        return None
800
801    def getValue( self, name ):
802        log.debug( "FileBrowseNode.getValue() called" )
803        return None
804
805    def hasMethod( self, name ):
806        return False
807
808    def hasProperty( self, name ):
809        return False
810
811    # XActionListener
812
813    def actionPerformed( self, event ):
814        try:
815            if event.ActionCommand == "Run":
816                code = self.editor.getControl("EditorTextField").getText()
817                code = ensureSourceState( code )
818                mod = imp.new_module("ooo_script_framework")
819                mod.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.provCtx.scriptContext
820                exec(code, mod.__dict__)
821                values = mod.__dict__.get( CALLABLE_CONTAINER_NAME , None )
822                if not values:
823                    values = list(mod.__dict__.values())
824
825                for i in values:
826                    if isScript( i ):
827                        i()
828                        break
829
830            elif event.ActionCommand == "Save":
831                toWrite = uno.ByteSequence(
832                    self.editor.getControl("EditorTextField").getText().encode("utf-8"))
833                copyUrl = self.uri() + ".orig"
834                self.provCtx.sfa.move( self.uri(), copyUrl )
835                log.debug( "Saving Python macro to URI " + self.uri() )
836                self.provCtx.sfa.writeFile( self.uri(), BytesInputStream( toWrite.value ) )
837                self.provCtx.sfa.kill( copyUrl )
838        except Exception as e:
839            # TODO: add an error box here !
840            log.error( lastException2String() )
841
842
843class DirBrowseNode( unohelper.Base, XBrowseNode, XPropertySet, XInvocation ):
844    def __init__( self, provCtx, name, rootUrl, depth ):
845        self.provCtx = provCtx
846        self.name = name
847        self.rootUrl = rootUrl
848        self.depth = depth
849        log.debug( "DirBrowseNode constructor for " + name + "," + rootUrl )
850
851    def getName( self ):
852        return self.name
853
854    def getChildNodes( self ):
855        try:
856            log.debug( "DirBrowseNode.getChildNodes called for " + self.rootUrl )
857            contents = self.provCtx.sfa.getFolderContents( self.rootUrl, True )
858            browseNodeList = []
859            for i in contents:
860                if i.endswith( ".py" ):
861                    log.debug( "adding filenode " + i )
862                    browseNodeList.append(
863                        FileBrowseNode( self.provCtx, self, i[i.rfind("/")+1:len(i)-3] ) )
864                elif self.provCtx.sfa.isFolder( i ) and not i.endswith("/pythonpath"):
865                    log.debug( "adding DirBrowseNode " + i )
866                    browseNodeList.append(
867                        DirBrowseNode( self.provCtx, i[i.rfind("/")+1:len(i)], i, self.depth + 1 ) )
868            return tuple( browseNodeList )
869        except Exception as e:
870            text = lastException2String()
871            log.error( "DirBrowseNode error: " + str(e) + " while evaluating " + self.rootUrl)
872            log.error( text)
873            return ()
874
875    def hasChildNodes( self ):
876        return True
877
878    def getType( self ):
879        return CONTAINER
880
881    # XScriptProvider
882
883    def getScript( self, uri ):
884        log.debug( "DirBrowseNode getScript " + uri + " invoked" )
885        raise IllegalArgumentException( "DirBrowseNode couldn't instantiate script " + uri , self , 0 )
886
887    # XPropertySet
888
889    def getPropertyValue( self, name ):
890        ret = None
891        try:
892            if name == "Creatable":
893                ret = True
894            elif name == "Deletable":
895                ret = self.depth > 0 and not self.provCtx.sfa.isReadOnly( self.rootUrl )
896            elif name == "Renamable":
897                ret = self.depth > 0 and not self.provCtx.sfa.isReadOnly( self.rootUrl )
898
899            log.debug( "DirBrowseNode.getPropertyValue called for " + name + ", returning " + str(ret) )
900        except Exception as e:
901            log.error( "DirBrowseNode.getPropertyValue error " + lastException2String())
902            raise
903
904        return ret
905
906    def setPropertyValue( self, name, value ):
907        log.debug( "DirBrowseNode.setPropertyValue called " + name + "=" +str(value ) )
908
909    def getPropertySetInfo( self ):
910        log.debug( "DirBrowseNode.getPropertySetInfo called "  )
911        return None
912
913    # XInvocation
914
915    def getIntrospection( self ):
916        log.debug( "DirBrowseNode.getIntrospection() called" )
917        return None
918
919    def invoke( self, name, params, outparamindex, outparams ):
920        log.debug("DirBrowseNode.invoke called for " + name + "," + str(params) + "," + str(outparamindex) + "," + str(outparams))
921        try:
922            if name == "Creatable":
923                if params is None or not params:
924                    raise IllegalArgumentException( "invoke with Creatable needs the name in params" )
925                if self.depth == 0:
926                    subFolderUrl = self.rootUrl + "/" + params[0]
927                    self.provCtx.sfa.createFolder( subFolderUrl )
928                    childNode = DirBrowseNode( self.provCtx, subFolderUrl[subFolderUrl.rfind("/")+1:len(subFolderUrl)], subFolderUrl, self.depth + 1 )
929                    return childNode, (), ()
930                else:
931                    scriptUrl = self.rootUrl + "/" + params[0] + ".py"
932                    # Creates an empty file
933                    self.provCtx.sfa.writeFile( scriptUrl, EmptyInputStream() )
934                    childNode = FileBrowseNode( self.provCtx, self, params[0] )
935                    return childNode, (), ()
936            elif name == "Deletable":
937                self.provCtx.sfa.kill( self.rootUrl )
938                return True, (), ()
939            elif name == "Renamable":
940                if params is None or not params:
941                    raise IllegalArgumentException( "invoke with Renamable needs the name in params" )
942                newUrl = self.rootUrl[0:self.rootUrl.rfind("/")+1] + params[0]
943                self.provCtx.sfa.move( self.rootUrl, newUrl )
944                self.rootUrl = newUrl
945                self.name = params[0]
946                return self, (), ()
947        except Exception as e:
948            log.error( "DirBrowseNode.invoke error: " + lastException2String())
949            raise
950        return None, (), ()
951
952    def setValue( self, name, value ):
953        return None
954
955    def getValue( self, name ):
956        log.debug( "DirBrowseNode.getValue() called" )
957        return None
958
959    def hasMethod( self, name ):
960        return False
961
962    def hasProperty( self, name ):
963        return False
964
965
966class ManifestHandler( XDocumentHandler, unohelper.Base ):
967    def __init__( self, rootUrl ):
968        self.rootUrl = rootUrl
969
970    def startDocument( self ):
971        self.urlList = []
972
973    def endDocument( self ):
974        pass
975
976    def startElement( self , name, attlist):
977        if name == "manifest:file-entry":
978            if attlist.getValueByName( "manifest:media-type" ) == "application/vnd.sun.star.framework-script":
979                self.urlList.append(
980                    self.rootUrl + "/" + attlist.getValueByName( "manifest:full-path" ) )
981
982    def endElement( self, name ):
983        pass
984
985    def characters ( self, chars ):
986        pass
987
988    def ignoreableWhitespace( self, chars ):
989        pass
990
991    def setDocumentLocator( self, locator ):
992        pass
993
994def isPyFileInPath( sfa, path ):
995    ret = False
996    contents = sfa.getFolderContents( path, True )
997    for i in contents:
998        if sfa.isFolder(i):
999            ret = isPyFileInPath(sfa,i)
1000        else:
1001            if i.endswith(".py"):
1002                ret = True
1003        if ret:
1004            break
1005    return ret
1006
1007# extracts META-INF directory from
1008def getPathesFromPackage( rootUrl, sfa ):
1009    ret = ()
1010    try:
1011        fileUrl = rootUrl + "/META-INF/manifest.xml"
1012        inputStream = sfa.openFileRead( fileUrl )
1013        parser = uno.getComponentContext().ServiceManager.createInstance( "com.sun.star.xml.sax.Parser" )
1014        handler = ManifestHandler( rootUrl )
1015        parser.setDocumentHandler( handler )
1016        parser.parseStream( InputSource( inputStream , "", fileUrl, fileUrl ) )
1017        for i in tuple(handler.urlList):
1018            if not isPyFileInPath( sfa, i ):
1019                handler.urlList.remove(i)
1020        ret = tuple( handler.urlList )
1021    except UnoException as e:
1022        text = lastException2String()
1023        log.debug( "getPathesFromPackage " + fileUrl + " Exception: " +text )
1024        pass
1025    return ret
1026
1027
1028class Package:
1029    def __init__( self, pathes, transientPathElement ):
1030        self.pathes = pathes
1031        self.transientPathElement = transientPathElement
1032
1033class DummyInteractionHandler( unohelper.Base, XInteractionHandler ):
1034    def __init__( self ):
1035        pass
1036    def handle( self, event):
1037        log.debug( "pythonscript: DummyInteractionHandler.handle " + str( event ) )
1038
1039class DummyProgressHandler( unohelper.Base, XProgressHandler ):
1040    def __init__( self ):
1041        pass
1042
1043    def push( self,status ):
1044        log.debug( "pythonscript: DummyProgressHandler.push " + str( status ) )
1045    def update( self,status ):
1046        log.debug( "pythonscript: DummyProgressHandler.update " + str( status ) )
1047    def pop( self ):
1048        log.debug( "pythonscript: DummyProgressHandler.push " + str( event ) )
1049
1050class CommandEnvironment(unohelper.Base, XCommandEnvironment):
1051    def __init__( self ):
1052        self.progressHandler = DummyProgressHandler()
1053        self.interactionHandler = DummyInteractionHandler()
1054    def getInteractionHandler( self ):
1055        return self.interactionHandler
1056    def getProgressHandler( self ):
1057        return self.progressHandler
1058
1059#maybe useful for debugging purposes
1060#class ModifyListener( unohelper.Base, XModifyListener ):
1061#    def __init__( self ):
1062#        pass
1063#    def modified( self, event ):
1064#        log.debug( "pythonscript: ModifyListener.modified " + str( event ) )
1065#    def disposing( self, event ):
1066#        log.debug( "pythonscript: ModifyListener.disposing " + str( event ) )
1067
1068def getModelFromDocUrl(ctx, url):
1069    """Get document model from document url."""
1070    doc = None
1071    args = ("Local", "Office")
1072    ucb = ctx.getServiceManager().createInstanceWithArgumentsAndContext(
1073        "com.sun.star.ucb.UniversalContentBroker", args, ctx)
1074    identifier = ucb.createContentIdentifier(url)
1075    content = ucb.queryContent(identifier)
1076    p = Property()
1077    p.Name = "DocumentModel"
1078    p.Handle = -1
1079
1080    c = Command()
1081    c.Handle = -1
1082    c.Name = "getPropertyValues"
1083    c.Argument = uno.Any("[]com.sun.star.beans.Property", (p,))
1084
1085    env = CommandEnvironment()
1086    try:
1087        ret = content.execute(c, 0, env)
1088        doc = ret.getObject(1, None)
1089    except Exception as e:
1090        log.isErrorLevel() and log.error("getModelFromDocUrl: %s" % url)
1091    return doc
1092
1093def mapStorageType2PackageContext( storageType ):
1094    ret = storageType
1095    if( storageType == "share:uno_packages" ):
1096        ret = "shared"
1097    if( storageType == "user:uno_packages" ):
1098        ret = "user"
1099    return ret
1100
1101def getPackageName2PathMap( sfa, storageType ):
1102    ret = {}
1103    packageManagerFactory = uno.getComponentContext().getValueByName(
1104        "/singletons/com.sun.star.deployment.thePackageManagerFactory" )
1105    packageManager = packageManagerFactory.getPackageManager(
1106        mapStorageType2PackageContext(storageType))
1107#    packageManager.addModifyListener( ModifyListener() )
1108    log.debug( "pythonscript: getPackageName2PathMap start getDeployedPackages" )
1109    packages = packageManager.getDeployedPackages(
1110        packageManager.createAbortChannel(), CommandEnvironment( ) )
1111    log.debug( "pythonscript: getPackageName2PathMap end getDeployedPackages (" + str(len(packages))+")" )
1112
1113    for i in packages:
1114        log.debug( "inspecting package " + i.Name + "("+i.Identifier.Value+")" )
1115        transientPathElement = penultimateElement( i.URL )
1116        j = expandUri( i.URL )
1117        pathes = getPathesFromPackage( j, sfa )
1118        if len( pathes ) > 0:
1119            # map package name to url, we need this later
1120            log.isErrorLevel() and log.error( "adding Package " + transientPathElement + " " + str( pathes ) )
1121            ret[ lastElement( j ) ] = Package( pathes, transientPathElement )
1122    return ret
1123
1124def penultimateElement( aStr ):
1125    lastSlash = aStr.rindex("/")
1126    penultimateSlash = aStr.rindex("/",0,lastSlash-1)
1127    return  aStr[ penultimateSlash+1:lastSlash ]
1128
1129def lastElement( aStr):
1130    return aStr[ aStr.rfind( "/" )+1:len(aStr)]
1131
1132class PackageBrowseNode( unohelper.Base, XBrowseNode, XPropertySet, XInvocation ):
1133    def __init__( self, provCtx, name, rootUrl ):
1134        self.provCtx = provCtx
1135        self.name = name
1136        self.rootUrl = rootUrl
1137
1138    def getName( self ):
1139        return self.name
1140
1141    def getChildNodes( self ):
1142        items = list(self.provCtx.mapPackageName2Path.items())
1143        browseNodeList = []
1144        for i in items:
1145            if len( i[1].pathes ) == 1:
1146                browseNodeList.append(
1147                    DirBrowseNode( self.provCtx, i[0], i[1].pathes[0], 0 ))
1148            else:
1149                for j in i[1].pathes:
1150                    browseNodeList.append(
1151                        DirBrowseNode( self.provCtx, i[0]+"."+lastElement(j), j, 0 ) )
1152        return tuple( browseNodeList )
1153
1154    def hasChildNodes( self ):
1155        return len( self.mapPackageName2Path ) > 0
1156
1157    def getType( self ):
1158        return CONTAINER
1159
1160    def getScript( self, uri ):
1161        log.debug( "DirBrowseNode getScript " + uri + " invoked" )
1162        raise IllegalArgumentException( "PackageBrowseNode couldn't instantiate script " + uri , self , 0 )
1163
1164    # XPropertySet
1165
1166    def getPropertyValue( self, name ):
1167        ret = None
1168        log.debug( "PackageBrowseNode.getPropertyValue called for " + name + ", returning " + str(ret) )
1169        return ret
1170
1171    def setPropertyValue( self, name, value ):
1172        log.debug( "PackageBrowseNode.setPropertyValue " + name + "=" +str( value ) )
1173
1174    def getPropertySetInfo( self ):
1175        log.debug( "PackageBrowseNode.getPropertySetInfo called" )
1176        return None
1177
1178    # XInvocation
1179
1180    def getIntrospection( self ):
1181        log.debug( "PackageBrowseNode.getIntrospection() called" )
1182        return None
1183
1184    def invoke( self, name, params, outparamindex, outparams ):
1185        log.debug( "PackageBrowseNode.invoke called for " + name + "," + str( params ) + "," + str( outparamindex ) + "," + str( outparams ) )
1186        return None, (), ()
1187
1188    def setValue( self, name, value ):
1189        log.debug( "PackageBrowseNode.setValue" )
1190        return None
1191
1192    def getValue( self, name ):
1193        log.debug( "PackageBrowseNode.getValue" )
1194        return None
1195
1196    def hasMethod( self, name ):
1197        log.debug( "PackageBrowseNode.hasMethod" )
1198        return False
1199
1200    def hasProperty( self, name ):
1201        log.debug( "PackageBrowseNode.hasProperty" )
1202        return False
1203
1204
1205
1206class PythonScript( unohelper.Base, XScript ):
1207    def __init__( self, func, mod ):
1208        self.func = func
1209        self.mod = mod
1210    def invoke(self, args, out, outindex ):
1211        log.debug( "PythonScript.invoke " + str( args ) )
1212        try:
1213            ret = self.func( *args )
1214        except UnoException as e:
1215            # UNO Exception continue to fly ...
1216            text = lastException2String()
1217            complete = "Error during invoking function " + \
1218                str(self.func.__name__) + " in module " + \
1219                self.mod.__file__ + " (" + text + ")"
1220            log.debug( complete )
1221            # some people may beat me up for modifying the exception text,
1222            # but otherwise office just shows
1223            # the type name and message text with no more information,
1224            # this is really bad for most users.
1225            e.Message = e.Message + " (" + complete + ")"
1226            raise
1227        except Exception as e:
1228            # General python exception are converted to uno RuntimeException
1229            text = lastException2String()
1230            complete = "Error during invoking function " + \
1231                str(self.func.__name__) + " in module " + \
1232                self.mod.__file__ + " (" + text + ")"
1233            log.debug( complete )
1234            raise RuntimeException( complete , self )
1235        log.debug( "PythonScript.invoke ret = " + str( ret ) )
1236        return ret, (), ()
1237
1238def expandUri(  uri ):
1239    if uri.startswith( "vnd.sun.star.expand:" ):
1240        uri = uri.replace( "vnd.sun.star.expand:", "",1)
1241        uri = uno.getComponentContext().getByName(
1242                    "/singletons/com.sun.star.util.theMacroExpander" ).expandMacros( uri )
1243    if uri.startswith( "file:" ):
1244        uri = uno.absolutize("",uri)   # necessary to get rid of .. in uri
1245    return uri
1246
1247#--------------------------------------------------------------
1248class PythonScriptProvider( unohelper.Base, XBrowseNode, XScriptProvider, XNameContainer, XPropertySet, XInvocation):
1249    def __init__( self, ctx, *args ):
1250        if log.isDebugLevel():
1251            mystr = ""
1252            for i in args:
1253                if len(mystr) > 0:
1254                    mystr = mystr +","
1255                mystr = mystr + str(i)
1256            log.debug( "Entering PythonScriptProvider.ctor with args " + mystr )
1257
1258        doc = None
1259        inv = None
1260        storageType = ""
1261
1262        if isinstance(args[0],unicode ):
1263            storageType = args[0]
1264            if storageType.startswith( "vnd.sun.star.tdoc" ):
1265                doc = getModelFromDocUrl(ctx, storageType)
1266        else:
1267            inv = args[0]
1268            try:
1269                doc = inv.ScriptContainer
1270                content = ctx.getServiceManager().createInstanceWithContext(
1271                    "com.sun.star.frame.TransientDocumentsDocumentContentFactory",
1272                    ctx).createDocumentContent(doc)
1273                storageType = content.getIdentifier().getContentIdentifier()
1274            except Exception as e:
1275                text = lastException2String()
1276                log.error( text )
1277
1278        isPackage = storageType.endswith( ":uno_packages" )
1279
1280        try:
1281#            urlHelper = ctx.ServiceManager.createInstanceWithArgumentsAndContext(
1282#                "com.sun.star.script.provider.ScriptURIHelper", (LANGUAGENAME, storageType), ctx)
1283            urlHelper = MyUriHelper( ctx, storageType )
1284            log.debug( "got urlHelper " + str( urlHelper ) )
1285
1286            rootUrl = expandUri( urlHelper.getRootStorageURI() )
1287            log.debug( storageType + " transformed to " + rootUrl )
1288
1289            ucbService = "com.sun.star.ucb.SimpleFileAccess"
1290            sfa = ctx.ServiceManager.createInstanceWithContext( ucbService, ctx )
1291            if not sfa:
1292                log.debug("PythonScriptProvider couldn't instantiate " +ucbService)
1293                raise RuntimeException(
1294                    "PythonScriptProvider couldn't instantiate " +ucbService, self)
1295            self.provCtx = ProviderContext(
1296                storageType, sfa, urlHelper, ScriptContext( uno.getComponentContext(), doc, inv ) )
1297            if isPackage:
1298                mapPackageName2Path = getPackageName2PathMap( sfa, storageType )
1299                self.provCtx.setPackageAttributes( mapPackageName2Path , rootUrl )
1300                self.dirBrowseNode = PackageBrowseNode( self.provCtx, LANGUAGENAME, rootUrl )
1301            else:
1302                self.dirBrowseNode = DirBrowseNode( self.provCtx, LANGUAGENAME, rootUrl, 0 )
1303
1304        except Exception as e:
1305            text = lastException2String()
1306            log.debug( "PythonScriptProvider could not be instantiated because of : " + text )
1307            raise e
1308
1309    def getName( self ):
1310        return self.dirBrowseNode.getName()
1311
1312    def getChildNodes( self ):
1313        return self.dirBrowseNode.getChildNodes()
1314
1315    def hasChildNodes( self ):
1316        return self.dirBrowseNode.hasChildNodes()
1317
1318    def getType( self ):
1319        return self.dirBrowseNode.getType()
1320
1321    def getScript( self, uri ):
1322        log.debug( "DirBrowseNode getScript " + uri + " invoked" )
1323
1324        raise IllegalArgumentException( "DirBrowseNode couldn't instantiate script " + uri , self , 0 )
1325
1326    def getScript( self, scriptUri ):
1327        try:
1328            log.debug( "getScript " + scriptUri + " invoked")
1329
1330            storageUri = self.provCtx.getStorageUrlFromPersistentUrl(
1331                self.provCtx.uriHelper.getStorageURI(scriptUri) );
1332            log.debug( "getScript: storageUri = " + storageUri)
1333            fileUri = storageUri[0:storageUri.find( "$" )]
1334            funcName = storageUri[storageUri.find( "$" )+1:len(storageUri)]
1335
1336            mod = self.provCtx.getModuleByUrl( fileUri )
1337            log.debug( " got mod " + str(mod) )
1338
1339            func = mod.__dict__[ funcName ]
1340
1341            log.debug( "got func " + str( func ) )
1342            return PythonScript( func, mod )
1343        except Exception as e:
1344            text = lastException2String()
1345            log.error( text )
1346            raise ScriptFrameworkErrorException( text, self, scriptUri, LANGUAGENAME, 0 )
1347
1348    # XPropertySet
1349
1350    def getPropertyValue( self, name ):
1351        return self.dirBrowseNode.getPropertyValue( name )
1352
1353    def setPropertyValue( self, name, value ):
1354        return self.dirBrowseNode.setPropertyValue( name, value )
1355
1356    def getPropertySetInfo( self ):
1357        return self.dirBrowseNode.getPropertySetInfo()
1358
1359    # XInvocation
1360
1361    def getIntrospection( self ):
1362        return self.dirBrowseNode.getIntrospection()
1363
1364    def invoke( self, name, params, outparamindex, outparams ):
1365        return self.dirBrowseNode.invoke( name, params, outparamindex, outparams)
1366
1367    def setValue( self, name, value ):
1368        return self.dirBrowseNode.setValue( name, value )
1369
1370    def getValue( self, name ):
1371        return self.dirBrowseNode.getValue( name )
1372
1373    def hasMethod( self, name ):
1374        return self.dirBrowseNode.hasMethod( name )
1375
1376    def hasProperty( self, name ):
1377        return self.dirBrowseNode.hasProperty( name )
1378
1379    # XServiceInfo
1380    def getSupportedServices( self ):
1381        return g_ImplementationHelper.getSupportedServices(g_implName)
1382
1383    def supportsService( self, ServiceName ):
1384        return g_ImplementationHelper.supportsService( g_implName, ServiceName )
1385
1386    def getImplementationName(self):
1387        return g_implName
1388
1389    def getByName( self, name ):
1390        log.debug( "getByName called" + str( name ))
1391        return None
1392
1393
1394    def getElementNames( self ):
1395        log.debug( "getElementNames called")
1396        return ()
1397
1398    def hasByName( self, name ):
1399        try:
1400            log.debug( "hasByName called " + str( name ))
1401            uri = expandUri(name)
1402            ret = self.provCtx.isUrlInPackage( uri )
1403            log.debug( "hasByName " + uri + " " +str( ret ) )
1404            return ret
1405        except Exception as e:
1406            text = lastException2String()
1407            log.debug( "Error in hasByName:" +  text )
1408            return False
1409
1410    def removeByName( self, name ):
1411        log.debug( "removeByName called" + str( name ))
1412        uri = expandUri( name )
1413        if self.provCtx.isUrlInPackage( uri ):
1414            self.provCtx.removePackageByUrl( uri )
1415        else:
1416            log.debug( "removeByName unknown uri " + str( name ) + ", ignoring" )
1417            raise NoSuchElementException( uri + "is not in package" , self )
1418        log.debug( "removeByName called" + str( uri ) + " successful" )
1419
1420    def insertByName( self, name, value ):
1421        log.debug( "insertByName called " + str( name ) + " " + str( value ))
1422        uri = expandUri( name )
1423        if isPyFileInPath( self.provCtx.sfa, uri ):
1424            self.provCtx.addPackageByUrl( uri )
1425        else:
1426            # package is no python package ...
1427            log.debug( "insertByName: no python files in " + str( uri ) + ", ignoring" )
1428            raise IllegalArgumentException( uri + " does not contain .py files", self, 1 )
1429        log.debug( "insertByName called " + str( uri ) + " successful" )
1430
1431    def replaceByName( self, name, value ):
1432        log.debug( "replaceByName called " + str( name ) + " " + str( value ))
1433        removeByName( name )
1434        insertByName( name )
1435        log.debug( "replaceByName called" + str( uri ) + " successful" )
1436
1437    def getElementType( self ):
1438        log.debug( "getElementType called" )
1439        return uno.getTypeByName( "void" )
1440
1441    def hasElements( self ):
1442        log.debug( "hasElements got called")
1443        return False
1444
1445g_ImplementationHelper.addImplementation( \
1446        PythonScriptProvider,g_implName, \
1447    ("com.sun.star.script.provider.LanguageScriptProvider",
1448     "com.sun.star.script.provider.ScriptProviderFor"+ LANGUAGENAME,),)
1449
1450
1451log.debug( "pythonscript finished initializing" )
1452