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