xref: /trunk/main/scripting/source/pyprov/pythonscript.py (revision d7d7c2bb2491c9ea01f6567c93f888a1a49f8f40)
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
505    # Forgets a stale module so a fresh copy can be loaded in the future.
506    # This is necessary because getModuleByUrl()'s self.sfa.getDateTimeModified()
507    # doesn't always work, eg. for embedded scripts it always returns timestamps
508    # with all zeroes, and even if it worked, the smallest granularity for ZIP file
509    # timestamps is 2 seconds, which isn't good enough.
510    def removeModuleByUrl( self, url ):
511        self.modules.pop( url, None )
512
513def createEditorDialog( ctx ):
514    smgr = ctx.ServiceManager
515
516    dialogModel = smgr.createInstanceWithContext(
517        "com.sun.star.awt.UnoControlDialogModel", ctx)
518    dialogModel.PositionX = 105
519    dialogModel.PositionY = 117
520    dialogModel.Width = 240
521    dialogModel.Height = 320
522    dialogModel.Closeable = True
523    dialogModel.Moveable = True
524    dialogModel.Title = "Python Macro Editor"
525
526    runButtonModel = dialogModel.createInstance(
527        "com.sun.star.awt.UnoControlButtonModel" )
528    runButtonModel.PositionX = 57
529    runButtonModel.PositionY = 300
530    runButtonModel.Width = 40
531    runButtonModel.Height = 14
532    runButtonModel.TabIndex = 0
533    runButtonModel.Label = "Run"
534
535    saveButtonModel = dialogModel.createInstance(
536        "com.sun.star.awt.UnoControlButtonModel" )
537    saveButtonModel.PositionX = 100
538    saveButtonModel.PositionY = 300
539    saveButtonModel.Width = 40
540    saveButtonModel.Height = 14
541    saveButtonModel.TabIndex = 1
542    saveButtonModel.Label = "Save"
543
544    closeButtonModel = dialogModel.createInstance(
545        "com.sun.star.awt.UnoControlButtonModel" )
546    closeButtonModel.PositionX = 143
547    closeButtonModel.PositionY = 300
548    closeButtonModel.Width = 40
549    closeButtonModel.Height = 14
550    closeButtonModel.TabIndex = 2
551    closeButtonModel.PushButtonType = 2  # CANCEL
552    closeButtonModel.Label = "Close"
553
554    textFieldModel = dialogModel.createInstance(
555        "com.sun.star.awt.UnoControlEditModel" )
556    textFieldModel.PositionX = 6
557    textFieldModel.PositionY = 6
558    textFieldModel.Width = 228
559    textFieldModel.Height = 288
560    textFieldModel.TabIndex = 3
561    textFieldModel.HScroll = True
562    textFieldModel.VScroll = True
563    textFieldModel.MultiLine = True
564
565    dialogModel.insertByName( "RunButton", runButtonModel )
566    dialogModel.insertByName( "SaveButton", saveButtonModel )
567    dialogModel.insertByName( "CloseButton", closeButtonModel )
568    dialogModel.insertByName( "EditorTextField", textFieldModel )
569
570    # create the dialog control and set the model
571    controlContainer = smgr.createInstanceWithContext(
572        "com.sun.star.awt.UnoControlDialog", ctx);
573    controlContainer.setModel(dialogModel);
574
575    # create a peer
576    toolkit = smgr.createInstanceWithContext(
577        "com.sun.star.awt.ExtToolkit", ctx);
578
579    controlContainer.setVisible(False);
580    controlContainer.createPeer(toolkit, None);
581
582    return controlContainer
583
584
585#--------------------------------------------------
586def isScript( candidate ):
587    ret = False
588    if isinstance( candidate, type(isScript) ):
589        ret = True
590    return ret
591
592#-------------------------------------------------------
593class ScriptBrowseNode( unohelper.Base, XBrowseNode, XPropertySet, XInvocation, XActionListener ):
594    def __init__( self, provCtx, parent, fileName, funcName ):
595        self.parent = parent
596        self.fileName = fileName
597        self.funcName = funcName
598        self.provCtx = provCtx
599
600    def uri( self ):
601        return self.parent.uri()
602
603    def getName( self ):
604        return self.funcName
605
606    def getChildNodes(self):
607        return ()
608
609    def hasChildNodes(self):
610        return False
611
612    def getType( self):
613        return SCRIPT
614
615    def getPropertyValue( self, name ):
616        ret = None
617        try:
618            if name == "URI":
619                ret = self.provCtx.uriHelper.getScriptURI(
620                    self.provCtx.getPersistentUrlFromStorageUrl( self.uri() + "$" + self.funcName ) )
621            elif name == "Editable":
622                ret = not self.provCtx.sfa.isReadOnly( self.uri() )
623
624            log.debug( "ScriptBrowseNode.getPropertyValue called for " + name + ", returning " + str(ret) )
625        except Exception as e:
626            log.error( "ScriptBrowseNode.getPropertyValue error " + lastException2String())
627            raise
628
629        return ret
630    def setPropertyValue( self, name, value ):
631        log.debug( "ScriptBrowseNode.setPropertyValue called " + name + "=" +str(value ) )
632    def getPropertySetInfo( self ):
633        log.debug( "ScriptBrowseNode.getPropertySetInfo called "  )
634        return None
635
636    def getIntrospection( self ):
637        return None
638
639    def invoke( self, name, params, outparamindex, outparams ):
640        if name == "Editable":
641            ctx = self.provCtx.scriptContext.getComponentContext()
642
643            self.editor = createEditorDialog( ctx )
644
645            code = readTextFromStream(self.provCtx.sfa.openFileRead(self.uri()))
646            code = ensureSourceState( code )
647            self.editor.getControl("EditorTextField").setText(code)
648
649            self.editor.getControl("RunButton").setActionCommand("Run")
650            self.editor.getControl("RunButton").addActionListener(self)
651            self.editor.getControl("SaveButton").setActionCommand("Save")
652            self.editor.getControl("SaveButton").addActionListener(self)
653
654            self.editor.execute()
655
656        return None, (), ()
657
658    def actionPerformed( self, event ):
659        try:
660            if event.ActionCommand == "Run":
661                code = self.editor.getControl("EditorTextField").getText()
662                code = ensureSourceState( code )
663                mod = imp.new_module("ooo_script_framework")
664                mod.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.provCtx.scriptContext
665                exec(code, mod.__dict__)
666                values = mod.__dict__.get( CALLABLE_CONTAINER_NAME , None )
667                if not values:
668                    values = list(mod.__dict__.values())
669
670                for i in values:
671                    if isScript( i ):
672                        i()
673                        break
674
675            elif event.ActionCommand == "Save":
676                toWrite = uno.ByteSequence(
677                    self.editor.getControl("EditorTextField").getText().encode("utf-8"))
678                log.debug( "Saving Python macro to URI " + self.uri() )
679                self.provCtx.sfa.writeFile( self.uri(), BytesInputStream( toWrite.value ) )
680                self.provCtx.removeModuleByUrl( self.uri() )
681        except Exception as e:
682            # TODO: add an error box here !
683            log.error( lastException2String() )
684
685
686    def setValue( self, name, value ):
687        return None
688
689    def getValue( self, name ):
690        return None
691
692    def hasMethod( self, name ):
693        return False
694
695    def hasProperty( self, name ):
696        return False
697
698
699#-------------------------------------------------------
700class FileBrowseNode( unohelper.Base, XBrowseNode, XPropertySet, XInvocation, XActionListener ):
701    def __init__( self, provCtx, parent, name ):
702        self.provCtx = provCtx
703        self.parent = parent
704        self.name = name
705        self.funcnames = None
706
707    def uri( self ):
708        return self.parent.rootUrl + "/" + self.name + ".py"
709
710    def getName( self ):
711        return self.name
712
713    def getChildNodes(self):
714        ret = ()
715        try:
716            self.funcnames = self.provCtx.getFuncsByUrl( self.uri() )
717
718            scriptNodeList = []
719            for i in self.funcnames:
720                scriptNodeList.append(
721                    ScriptBrowseNode(
722                    self.provCtx, self, self.name, i ))
723            ret = tuple( scriptNodeList )
724        except Exception as e:
725            text = lastException2String()
726            log.error( "FileBrowseNode.getChildNodes error while evaluating " + self.uri() + ":" + text )
727            raise
728        return ret
729
730    def hasChildNodes(self):
731        try:
732            return len(self.getChildNodes()) > 0
733        except Exception as e:
734            return False
735
736    def getType( self):
737        return CONTAINER
738
739    # XPropertySet
740
741    def getPropertyValue( self, name ):
742        ret = None
743        try:
744            if name == "Editable":
745                ret = not self.provCtx.sfa.isReadOnly( self.uri() )
746            elif name == "Deletable":
747                ret = not self.provCtx.sfa.isReadOnly( self.uri() )
748            elif name == "Renamable":
749                ret = not self.provCtx.sfa.isReadOnly( self.uri() )
750
751            log.debug( "FileBrowseNode.getPropertyValue called for " + name + ", returning " + str(ret) )
752        except Exception as e:
753            log.error( "FileBrowseNode.getPropertyValue error " + lastException2String())
754            raise
755
756        return ret
757
758    def setPropertyValue( self, name, value ):
759        log.debug( "FileBrowseNode.setPropertyValue called " + name + "=" +str(value ) )
760
761    def getPropertySetInfo( self ):
762        log.debug( "FileBrowseNode.getPropertySetInfo called "  )
763        return None
764
765    # XInvocation
766
767    def getIntrospection( self ):
768        log.debug( "FileBrowseNode.getIntrospection() called" )
769        return None
770
771    def invoke( self, name, params, outparamindex, outparams ):
772        log.debug("FileBrowseNode.invoke called for " + name + "," + str(params) + "," + str(outparamindex) + "," + str(outparams))
773        try:
774            if name == "Editable":
775                ctx = self.provCtx.scriptContext.getComponentContext()
776
777                self.editor = createEditorDialog( ctx )
778
779                code = readTextFromStream(self.provCtx.sfa.openFileRead(self.uri()))
780                code = ensureSourceState( code )
781                self.editor.getControl("EditorTextField").setText(code)
782
783                self.editor.getControl("RunButton").setActionCommand("Run")
784                self.editor.getControl("RunButton").addActionListener(self)
785                self.editor.getControl("SaveButton").setActionCommand("Save")
786                self.editor.getControl("SaveButton").addActionListener(self)
787
788                self.editor.execute()
789            elif name == "Deletable":
790                self.provCtx.sfa.kill( self.uri() )
791                return True, (), ()
792            elif name == "Renamable":
793                if params is None or not params:
794                    raise IllegalArgumentException( "invoke with Creatable needs the name in params" )
795                newUri = self.parent.rootUrl + "/" + params[0] + ".py"
796                self.provCtx.sfa.move( self.uri(), newUri )
797                self.name = params[0]
798                return self, (), ()
799        except Exception as e:
800            log.error( "FileBrowseNode.invoke error " + lastException2String() )
801            raise
802        return None, (), ()
803
804    def setValue( self, name, value ):
805        return None
806
807    def getValue( self, name ):
808        log.debug( "FileBrowseNode.getValue() called" )
809        return None
810
811    def hasMethod( self, name ):
812        return False
813
814    def hasProperty( self, name ):
815        return False
816
817    # XActionListener
818
819    def actionPerformed( self, event ):
820        try:
821            if event.ActionCommand == "Run":
822                code = self.editor.getControl("EditorTextField").getText()
823                code = ensureSourceState( code )
824                mod = imp.new_module("ooo_script_framework")
825                mod.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.provCtx.scriptContext
826                exec(code, mod.__dict__)
827                values = mod.__dict__.get( CALLABLE_CONTAINER_NAME , None )
828                if not values:
829                    values = list(mod.__dict__.values())
830
831                for i in values:
832                    if isScript( i ):
833                        i()
834                        break
835
836            elif event.ActionCommand == "Save":
837                toWrite = uno.ByteSequence(
838                    self.editor.getControl("EditorTextField").getText().encode("utf-8"))
839                log.debug( "Saving Python macro to URI " + self.uri() )
840                self.provCtx.sfa.writeFile( self.uri(), BytesInputStream( toWrite.value ) )
841                self.provCtx.removeModuleByUrl( self.uri() )
842        except Exception as e:
843            # TODO: add an error box here !
844            log.error( lastException2String() )
845
846
847class DirBrowseNode( unohelper.Base, XBrowseNode, XPropertySet, XInvocation ):
848    def __init__( self, provCtx, name, rootUrl, depth ):
849        self.provCtx = provCtx
850        self.name = name
851        self.rootUrl = rootUrl
852        self.depth = depth
853        log.debug( "DirBrowseNode constructor for " + name + "," + rootUrl )
854
855    def getName( self ):
856        return self.name
857
858    def getChildNodes( self ):
859        try:
860            log.debug( "DirBrowseNode.getChildNodes called for " + self.rootUrl )
861            contents = self.provCtx.sfa.getFolderContents( self.rootUrl, True )
862            browseNodeList = []
863            for i in contents:
864                if i.endswith( ".py" ):
865                    log.debug( "adding filenode " + i )
866                    browseNodeList.append(
867                        FileBrowseNode( self.provCtx, self, i[i.rfind("/")+1:len(i)-3] ) )
868                elif self.provCtx.sfa.isFolder( i ) and not i.endswith("/pythonpath"):
869                    log.debug( "adding DirBrowseNode " + i )
870                    browseNodeList.append(
871                        DirBrowseNode( self.provCtx, i[i.rfind("/")+1:len(i)], i, self.depth + 1 ) )
872            return tuple( browseNodeList )
873        except Exception as e:
874            text = lastException2String()
875            log.error( "DirBrowseNode error: " + str(e) + " while evaluating " + self.rootUrl)
876            log.error( text)
877            return ()
878
879    def hasChildNodes( self ):
880        return True
881
882    def getType( self ):
883        return CONTAINER
884
885    # XScriptProvider
886
887    def getScript( self, uri ):
888        log.debug( "DirBrowseNode getScript " + uri + " invoked" )
889        raise IllegalArgumentException( "DirBrowseNode couldn't instantiate script " + uri , self , 0 )
890
891    # XPropertySet
892
893    def getPropertyValue( self, name ):
894        ret = None
895        try:
896            if name == "Creatable":
897                ret = True
898            elif name == "Deletable":
899                ret = self.depth > 0 and not self.provCtx.sfa.isReadOnly( self.rootUrl )
900            elif name == "Renamable":
901                ret = self.depth > 0 and not self.provCtx.sfa.isReadOnly( self.rootUrl )
902
903            log.debug( "DirBrowseNode.getPropertyValue called for " + name + ", returning " + str(ret) )
904        except Exception as e:
905            log.error( "DirBrowseNode.getPropertyValue error " + lastException2String())
906            raise
907
908        return ret
909
910    def setPropertyValue( self, name, value ):
911        log.debug( "DirBrowseNode.setPropertyValue called " + name + "=" +str(value ) )
912
913    def getPropertySetInfo( self ):
914        log.debug( "DirBrowseNode.getPropertySetInfo called "  )
915        return None
916
917    # XInvocation
918
919    def getIntrospection( self ):
920        log.debug( "DirBrowseNode.getIntrospection() called" )
921        return None
922
923    def invoke( self, name, params, outparamindex, outparams ):
924        log.debug("DirBrowseNode.invoke called for " + name + "," + str(params) + "," + str(outparamindex) + "," + str(outparams))
925        try:
926            if name == "Creatable":
927                if params is None or not params:
928                    raise IllegalArgumentException( "invoke with Creatable needs the name in params" )
929                if self.depth == 0:
930                    subFolderUrl = self.rootUrl + "/" + params[0]
931                    self.provCtx.sfa.createFolder( subFolderUrl )
932                    childNode = DirBrowseNode( self.provCtx, subFolderUrl[subFolderUrl.rfind("/")+1:len(subFolderUrl)], subFolderUrl, self.depth + 1 )
933                    return childNode, (), ()
934                else:
935                    scriptUrl = self.rootUrl + "/" + params[0] + ".py"
936                    # Creates an empty file
937                    self.provCtx.sfa.writeFile( scriptUrl, EmptyInputStream() )
938                    childNode = FileBrowseNode( self.provCtx, self, params[0] )
939                    return childNode, (), ()
940            elif name == "Deletable":
941                self.provCtx.sfa.kill( self.rootUrl )
942                return True, (), ()
943            elif name == "Renamable":
944                if params is None or not params:
945                    raise IllegalArgumentException( "invoke with Renamable needs the name in params" )
946                newUrl = self.rootUrl[0:self.rootUrl.rfind("/")+1] + params[0]
947                self.provCtx.sfa.move( self.rootUrl, newUrl )
948                self.rootUrl = newUrl
949                self.name = params[0]
950                return self, (), ()
951        except Exception as e:
952            log.error( "DirBrowseNode.invoke error: " + lastException2String())
953            raise
954        return None, (), ()
955
956    def setValue( self, name, value ):
957        return None
958
959    def getValue( self, name ):
960        log.debug( "DirBrowseNode.getValue() called" )
961        return None
962
963    def hasMethod( self, name ):
964        return False
965
966    def hasProperty( self, name ):
967        return False
968
969
970class ManifestHandler( XDocumentHandler, unohelper.Base ):
971    def __init__( self, rootUrl ):
972        self.rootUrl = rootUrl
973
974    def startDocument( self ):
975        self.urlList = []
976
977    def endDocument( self ):
978        pass
979
980    def startElement( self , name, attlist):
981        if name == "manifest:file-entry":
982            if attlist.getValueByName( "manifest:media-type" ) == "application/vnd.sun.star.framework-script":
983                self.urlList.append(
984                    self.rootUrl + "/" + attlist.getValueByName( "manifest:full-path" ) )
985
986    def endElement( self, name ):
987        pass
988
989    def characters ( self, chars ):
990        pass
991
992    def ignoreableWhitespace( self, chars ):
993        pass
994
995    def setDocumentLocator( self, locator ):
996        pass
997
998def isPyFileInPath( sfa, path ):
999    ret = False
1000    contents = sfa.getFolderContents( path, True )
1001    for i in contents:
1002        if sfa.isFolder(i):
1003            ret = isPyFileInPath(sfa,i)
1004        else:
1005            if i.endswith(".py"):
1006                ret = True
1007        if ret:
1008            break
1009    return ret
1010
1011# extracts META-INF directory from
1012def getPathesFromPackage( rootUrl, sfa ):
1013    ret = ()
1014    try:
1015        fileUrl = rootUrl + "/META-INF/manifest.xml"
1016        inputStream = sfa.openFileRead( fileUrl )
1017        parser = uno.getComponentContext().ServiceManager.createInstance( "com.sun.star.xml.sax.Parser" )
1018        handler = ManifestHandler( rootUrl )
1019        parser.setDocumentHandler( handler )
1020        parser.parseStream( InputSource( inputStream , "", fileUrl, fileUrl ) )
1021        for i in tuple(handler.urlList):
1022            if not isPyFileInPath( sfa, i ):
1023                handler.urlList.remove(i)
1024        ret = tuple( handler.urlList )
1025    except UnoException as e:
1026        text = lastException2String()
1027        log.debug( "getPathesFromPackage " + fileUrl + " Exception: " +text )
1028        pass
1029    return ret
1030
1031
1032class Package:
1033    def __init__( self, pathes, transientPathElement ):
1034        self.pathes = pathes
1035        self.transientPathElement = transientPathElement
1036
1037class DummyInteractionHandler( unohelper.Base, XInteractionHandler ):
1038    def __init__( self ):
1039        pass
1040    def handle( self, event):
1041        log.debug( "pythonscript: DummyInteractionHandler.handle " + str( event ) )
1042
1043class DummyProgressHandler( unohelper.Base, XProgressHandler ):
1044    def __init__( self ):
1045        pass
1046
1047    def push( self,status ):
1048        log.debug( "pythonscript: DummyProgressHandler.push " + str( status ) )
1049    def update( self,status ):
1050        log.debug( "pythonscript: DummyProgressHandler.update " + str( status ) )
1051    def pop( self ):
1052        log.debug( "pythonscript: DummyProgressHandler.push " + str( event ) )
1053
1054class CommandEnvironment(unohelper.Base, XCommandEnvironment):
1055    def __init__( self ):
1056        self.progressHandler = DummyProgressHandler()
1057        self.interactionHandler = DummyInteractionHandler()
1058    def getInteractionHandler( self ):
1059        return self.interactionHandler
1060    def getProgressHandler( self ):
1061        return self.progressHandler
1062
1063#maybe useful for debugging purposes
1064#class ModifyListener( unohelper.Base, XModifyListener ):
1065#    def __init__( self ):
1066#        pass
1067#    def modified( self, event ):
1068#        log.debug( "pythonscript: ModifyListener.modified " + str( event ) )
1069#    def disposing( self, event ):
1070#        log.debug( "pythonscript: ModifyListener.disposing " + str( event ) )
1071
1072def getModelFromDocUrl(ctx, url):
1073    """Get document model from document url."""
1074    doc = None
1075    args = ("Local", "Office")
1076    ucb = ctx.getServiceManager().createInstanceWithArgumentsAndContext(
1077        "com.sun.star.ucb.UniversalContentBroker", args, ctx)
1078    identifier = ucb.createContentIdentifier(url)
1079    content = ucb.queryContent(identifier)
1080    p = Property()
1081    p.Name = "DocumentModel"
1082    p.Handle = -1
1083
1084    c = Command()
1085    c.Handle = -1
1086    c.Name = "getPropertyValues"
1087    c.Argument = uno.Any("[]com.sun.star.beans.Property", (p,))
1088
1089    env = CommandEnvironment()
1090    try:
1091        ret = content.execute(c, 0, env)
1092        doc = ret.getObject(1, None)
1093    except Exception as e:
1094        log.isErrorLevel() and log.error("getModelFromDocUrl: %s" % url)
1095    return doc
1096
1097def mapStorageType2PackageContext( storageType ):
1098    ret = storageType
1099    if( storageType == "share:uno_packages" ):
1100        ret = "shared"
1101    if( storageType == "user:uno_packages" ):
1102        ret = "user"
1103    return ret
1104
1105def getPackageName2PathMap( sfa, storageType ):
1106    ret = {}
1107    packageManagerFactory = uno.getComponentContext().getValueByName(
1108        "/singletons/com.sun.star.deployment.thePackageManagerFactory" )
1109    packageManager = packageManagerFactory.getPackageManager(
1110        mapStorageType2PackageContext(storageType))
1111#    packageManager.addModifyListener( ModifyListener() )
1112    log.debug( "pythonscript: getPackageName2PathMap start getDeployedPackages" )
1113    packages = packageManager.getDeployedPackages(
1114        packageManager.createAbortChannel(), CommandEnvironment( ) )
1115    log.debug( "pythonscript: getPackageName2PathMap end getDeployedPackages (" + str(len(packages))+")" )
1116
1117    for i in packages:
1118        log.debug( "inspecting package " + i.Name + "("+i.Identifier.Value+")" )
1119        transientPathElement = penultimateElement( i.URL )
1120        j = expandUri( i.URL )
1121        pathes = getPathesFromPackage( j, sfa )
1122        if len( pathes ) > 0:
1123            # map package name to url, we need this later
1124            log.isErrorLevel() and log.error( "adding Package " + transientPathElement + " " + str( pathes ) )
1125            ret[ lastElement( j ) ] = Package( pathes, transientPathElement )
1126    return ret
1127
1128def penultimateElement( aStr ):
1129    lastSlash = aStr.rindex("/")
1130    penultimateSlash = aStr.rindex("/",0,lastSlash-1)
1131    return  aStr[ penultimateSlash+1:lastSlash ]
1132
1133def lastElement( aStr):
1134    return aStr[ aStr.rfind( "/" )+1:len(aStr)]
1135
1136class PackageBrowseNode( unohelper.Base, XBrowseNode, XPropertySet, XInvocation ):
1137    def __init__( self, provCtx, name, rootUrl ):
1138        self.provCtx = provCtx
1139        self.name = name
1140        self.rootUrl = rootUrl
1141
1142    def getName( self ):
1143        return self.name
1144
1145    def getChildNodes( self ):
1146        items = list(self.provCtx.mapPackageName2Path.items())
1147        browseNodeList = []
1148        for i in items:
1149            if len( i[1].pathes ) == 1:
1150                browseNodeList.append(
1151                    DirBrowseNode( self.provCtx, i[0], i[1].pathes[0], 0 ))
1152            else:
1153                for j in i[1].pathes:
1154                    browseNodeList.append(
1155                        DirBrowseNode( self.provCtx, i[0]+"."+lastElement(j), j, 0 ) )
1156        return tuple( browseNodeList )
1157
1158    def hasChildNodes( self ):
1159        return len( self.mapPackageName2Path ) > 0
1160
1161    def getType( self ):
1162        return CONTAINER
1163
1164    def getScript( self, uri ):
1165        log.debug( "DirBrowseNode getScript " + uri + " invoked" )
1166        raise IllegalArgumentException( "PackageBrowseNode couldn't instantiate script " + uri , self , 0 )
1167
1168    # XPropertySet
1169
1170    def getPropertyValue( self, name ):
1171        ret = None
1172        log.debug( "PackageBrowseNode.getPropertyValue called for " + name + ", returning " + str(ret) )
1173        return ret
1174
1175    def setPropertyValue( self, name, value ):
1176        log.debug( "PackageBrowseNode.setPropertyValue " + name + "=" +str( value ) )
1177
1178    def getPropertySetInfo( self ):
1179        log.debug( "PackageBrowseNode.getPropertySetInfo called" )
1180        return None
1181
1182    # XInvocation
1183
1184    def getIntrospection( self ):
1185        log.debug( "PackageBrowseNode.getIntrospection() called" )
1186        return None
1187
1188    def invoke( self, name, params, outparamindex, outparams ):
1189        log.debug( "PackageBrowseNode.invoke called for " + name + "," + str( params ) + "," + str( outparamindex ) + "," + str( outparams ) )
1190        return None, (), ()
1191
1192    def setValue( self, name, value ):
1193        log.debug( "PackageBrowseNode.setValue" )
1194        return None
1195
1196    def getValue( self, name ):
1197        log.debug( "PackageBrowseNode.getValue" )
1198        return None
1199
1200    def hasMethod( self, name ):
1201        log.debug( "PackageBrowseNode.hasMethod" )
1202        return False
1203
1204    def hasProperty( self, name ):
1205        log.debug( "PackageBrowseNode.hasProperty" )
1206        return False
1207
1208
1209
1210class PythonScript( unohelper.Base, XScript ):
1211    def __init__( self, func, mod ):
1212        self.func = func
1213        self.mod = mod
1214    def invoke(self, args, out, outindex ):
1215        log.debug( "PythonScript.invoke " + str( args ) )
1216        try:
1217            ret = self.func( *args )
1218        except UnoException as e:
1219            # UNO Exception continue to fly ...
1220            text = lastException2String()
1221            complete = "Error during invoking function " + \
1222                str(self.func.__name__) + " in module " + \
1223                self.mod.__file__ + " (" + text + ")"
1224            log.debug( complete )
1225            # some people may beat me up for modifying the exception text,
1226            # but otherwise office just shows
1227            # the type name and message text with no more information,
1228            # this is really bad for most users.
1229            e.Message = e.Message + " (" + complete + ")"
1230            raise
1231        except Exception as e:
1232            # General python exception are converted to uno RuntimeException
1233            text = lastException2String()
1234            complete = "Error during invoking function " + \
1235                str(self.func.__name__) + " in module " + \
1236                self.mod.__file__ + " (" + text + ")"
1237            log.debug( complete )
1238            raise RuntimeException( complete , self )
1239        log.debug( "PythonScript.invoke ret = " + str( ret ) )
1240        return ret, (), ()
1241
1242def expandUri(  uri ):
1243    if uri.startswith( "vnd.sun.star.expand:" ):
1244        uri = uri.replace( "vnd.sun.star.expand:", "",1)
1245        uri = uno.getComponentContext().getByName(
1246                    "/singletons/com.sun.star.util.theMacroExpander" ).expandMacros( uri )
1247    if uri.startswith( "file:" ):
1248        uri = uno.absolutize("",uri)   # necessary to get rid of .. in uri
1249    return uri
1250
1251#--------------------------------------------------------------
1252class PythonScriptProvider( unohelper.Base, XBrowseNode, XScriptProvider, XNameContainer, XPropertySet, XInvocation):
1253    def __init__( self, ctx, *args ):
1254        if log.isDebugLevel():
1255            mystr = ""
1256            for i in args:
1257                if len(mystr) > 0:
1258                    mystr = mystr +","
1259                mystr = mystr + str(i)
1260            log.debug( "Entering PythonScriptProvider.ctor with args " + mystr )
1261
1262        doc = None
1263        inv = None
1264        storageType = ""
1265
1266        if isinstance(args[0],unicode ):
1267            storageType = args[0]
1268            if storageType.startswith( "vnd.sun.star.tdoc" ):
1269                doc = getModelFromDocUrl(ctx, storageType)
1270        else:
1271            inv = args[0]
1272            try:
1273                doc = inv.ScriptContainer
1274                content = ctx.getServiceManager().createInstanceWithContext(
1275                    "com.sun.star.frame.TransientDocumentsDocumentContentFactory",
1276                    ctx).createDocumentContent(doc)
1277                storageType = content.getIdentifier().getContentIdentifier()
1278            except Exception as e:
1279                text = lastException2String()
1280                log.error( text )
1281
1282        isPackage = storageType.endswith( ":uno_packages" )
1283
1284        try:
1285#            urlHelper = ctx.ServiceManager.createInstanceWithArgumentsAndContext(
1286#                "com.sun.star.script.provider.ScriptURIHelper", (LANGUAGENAME, storageType), ctx)
1287            urlHelper = MyUriHelper( ctx, storageType )
1288            log.debug( "got urlHelper " + str( urlHelper ) )
1289
1290            rootUrl = expandUri( urlHelper.getRootStorageURI() )
1291            log.debug( storageType + " transformed to " + rootUrl )
1292
1293            ucbService = "com.sun.star.ucb.SimpleFileAccess"
1294            sfa = ctx.ServiceManager.createInstanceWithContext( ucbService, ctx )
1295            if not sfa:
1296                log.debug("PythonScriptProvider couldn't instantiate " +ucbService)
1297                raise RuntimeException(
1298                    "PythonScriptProvider couldn't instantiate " +ucbService, self)
1299            self.provCtx = ProviderContext(
1300                storageType, sfa, urlHelper, ScriptContext( uno.getComponentContext(), doc, inv ) )
1301            if isPackage:
1302                mapPackageName2Path = getPackageName2PathMap( sfa, storageType )
1303                self.provCtx.setPackageAttributes( mapPackageName2Path , rootUrl )
1304                self.dirBrowseNode = PackageBrowseNode( self.provCtx, LANGUAGENAME, rootUrl )
1305            else:
1306                self.dirBrowseNode = DirBrowseNode( self.provCtx, LANGUAGENAME, rootUrl, 0 )
1307
1308        except Exception as e:
1309            text = lastException2String()
1310            log.debug( "PythonScriptProvider could not be instantiated because of : " + text )
1311            raise e
1312
1313    def getName( self ):
1314        return self.dirBrowseNode.getName()
1315
1316    def getChildNodes( self ):
1317        return self.dirBrowseNode.getChildNodes()
1318
1319    def hasChildNodes( self ):
1320        return self.dirBrowseNode.hasChildNodes()
1321
1322    def getType( self ):
1323        return self.dirBrowseNode.getType()
1324
1325    def getScript( self, scriptUri ):
1326        try:
1327            log.debug( "DirBrowseNode getScript " + scriptUri + " invoked")
1328
1329            storageUri = self.provCtx.getStorageUrlFromPersistentUrl(
1330                self.provCtx.uriHelper.getStorageURI(scriptUri) );
1331            log.debug( "getScript: storageUri = " + storageUri)
1332            fileUri = storageUri[0:storageUri.find( "$" )]
1333            funcName = storageUri[storageUri.find( "$" )+1:len(storageUri)]
1334
1335            mod = self.provCtx.getModuleByUrl( fileUri )
1336            log.debug( " got mod " + str(mod) )
1337
1338            func = mod.__dict__[ funcName ]
1339
1340            log.debug( "got func " + str( func ) )
1341            return PythonScript( func, mod )
1342        except Exception as e:
1343            text = lastException2String()
1344            log.error( text )
1345            raise ScriptFrameworkErrorException( text, self, scriptUri, LANGUAGENAME, 0 )
1346
1347    # XPropertySet
1348
1349    def getPropertyValue( self, name ):
1350        return self.dirBrowseNode.getPropertyValue( name )
1351
1352    def setPropertyValue( self, name, value ):
1353        return self.dirBrowseNode.setPropertyValue( name, value )
1354
1355    def getPropertySetInfo( self ):
1356        return self.dirBrowseNode.getPropertySetInfo()
1357
1358    # XInvocation
1359
1360    def getIntrospection( self ):
1361        return self.dirBrowseNode.getIntrospection()
1362
1363    def invoke( self, name, params, outparamindex, outparams ):
1364        return self.dirBrowseNode.invoke( name, params, outparamindex, outparams)
1365
1366    def setValue( self, name, value ):
1367        return self.dirBrowseNode.setValue( name, value )
1368
1369    def getValue( self, name ):
1370        return self.dirBrowseNode.getValue( name )
1371
1372    def hasMethod( self, name ):
1373        return self.dirBrowseNode.hasMethod( name )
1374
1375    def hasProperty( self, name ):
1376        return self.dirBrowseNode.hasProperty( name )
1377
1378    # XServiceInfo
1379    def getSupportedServices( self ):
1380        return g_ImplementationHelper.getSupportedServices(g_implName)
1381
1382    def supportsService( self, ServiceName ):
1383        return g_ImplementationHelper.supportsService( g_implName, ServiceName )
1384
1385    def getImplementationName(self):
1386        return g_implName
1387
1388    def getByName( self, name ):
1389        log.debug( "getByName called" + str( name ))
1390        return None
1391
1392
1393    def getElementNames( self ):
1394        log.debug( "getElementNames called")
1395        return ()
1396
1397    def hasByName( self, name ):
1398        try:
1399            log.debug( "hasByName called " + str( name ))
1400            uri = expandUri(name)
1401            ret = self.provCtx.isUrlInPackage( uri )
1402            log.debug( "hasByName " + uri + " " +str( ret ) )
1403            return ret
1404        except Exception as e:
1405            text = lastException2String()
1406            log.debug( "Error in hasByName:" +  text )
1407            return False
1408
1409    def removeByName( self, name ):
1410        log.debug( "removeByName called" + str( name ))
1411        uri = expandUri( name )
1412        if self.provCtx.isUrlInPackage( uri ):
1413            self.provCtx.removePackageByUrl( uri )
1414        else:
1415            log.debug( "removeByName unknown uri " + str( name ) + ", ignoring" )
1416            raise NoSuchElementException( uri + "is not in package" , self )
1417        log.debug( "removeByName called" + str( uri ) + " successful" )
1418
1419    def insertByName( self, name, value ):
1420        log.debug( "insertByName called " + str( name ) + " " + str( value ))
1421        uri = expandUri( name )
1422        if isPyFileInPath( self.provCtx.sfa, uri ):
1423            self.provCtx.addPackageByUrl( uri )
1424        else:
1425            # package is no python package ...
1426            log.debug( "insertByName: no python files in " + str( uri ) + ", ignoring" )
1427            raise IllegalArgumentException( uri + " does not contain .py files", self, 1 )
1428        log.debug( "insertByName called " + str( uri ) + " successful" )
1429
1430    def replaceByName( self, name, value ):
1431        log.debug( "replaceByName called " + str( name ) + " " + str( value ))
1432        removeByName( name )
1433        insertByName( name )
1434        log.debug( "replaceByName called" + str( uri ) + " successful" )
1435
1436    def getElementType( self ):
1437        log.debug( "getElementType called" )
1438        return uno.getTypeByName( "void" )
1439
1440    def hasElements( self ):
1441        log.debug( "hasElements got called")
1442        return False
1443
1444g_ImplementationHelper.addImplementation( \
1445        PythonScriptProvider,g_implName, \
1446    ("com.sun.star.script.provider.LanguageScriptProvider",
1447     "com.sun.star.script.provider.ScriptProviderFor"+ LANGUAGENAME,),)
1448
1449
1450log.debug( "pythonscript finished initializing" )
1451