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