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