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