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