19f22d7c2SAndrew Rist# *************************************************************
29f22d7c2SAndrew Rist#
39f22d7c2SAndrew Rist#  Licensed to the Apache Software Foundation (ASF) under one
49f22d7c2SAndrew Rist#  or more contributor license agreements.  See the NOTICE file
59f22d7c2SAndrew Rist#  distributed with this work for additional information
69f22d7c2SAndrew Rist#  regarding copyright ownership.  The ASF licenses this file
79f22d7c2SAndrew Rist#  to you under the Apache License, Version 2.0 (the
89f22d7c2SAndrew Rist#  "License"); you may not use this file except in compliance
99f22d7c2SAndrew Rist#  with the License.  You may obtain a copy of the License at
109f22d7c2SAndrew Rist#
119f22d7c2SAndrew Rist#    http://www.apache.org/licenses/LICENSE-2.0
129f22d7c2SAndrew Rist#
139f22d7c2SAndrew Rist#  Unless required by applicable law or agreed to in writing,
149f22d7c2SAndrew Rist#  software distributed under the License is distributed on an
159f22d7c2SAndrew Rist#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
169f22d7c2SAndrew Rist#  KIND, either express or implied.  See the License for the
179f22d7c2SAndrew Rist#  specific language governing permissions and limitations
189f22d7c2SAndrew Rist#  under the License.
199f22d7c2SAndrew Rist#
209f22d7c2SAndrew Rist# *************************************************************
219f22d7c2SAndrew Rist
22cdf0e10cSrcweir# XScript implementation for python
23cdf0e10cSrcweirimport uno
24cdf0e10cSrcweirimport unohelper
25cdf0e10cSrcweirimport sys
26cdf0e10cSrcweirimport os
27cdf0e10cSrcweirimport imp
28cdf0e10cSrcweirimport time
2913cfd8dfSPedro Giffuniimport ast
30cdf0e10cSrcweir
31*15745147SPedro Giffunitry:
32*15745147SPedro Giffuni    unicode
33*15745147SPedro Giffuniexcept NameError:
34*15745147SPedro Giffuni    unicode = str
35*15745147SPedro Giffuni
36cdf0e10cSrcweirclass LogLevel:
37cdf0e10cSrcweir    NONE = 0
38cdf0e10cSrcweir    ERROR = 1
39cdf0e10cSrcweir    DEBUG = 2
40cdf0e10cSrcweir
41cdf0e10cSrcweir# Configuration ----------------------------------------------------
42cdf0e10cSrcweirLogLevel.use = LogLevel.NONE                # production level
43cdf0e10cSrcweir#LogLevel.use = LogLevel.ERROR               # for script developers
44cdf0e10cSrcweir#LogLevel.use = LogLevel.DEBUG               # for script framework developers
45cdf0e10cSrcweirLOG_STDOUT = True                           # True, writes to stdout (difficult on windows)
46cdf0e10cSrcweir                                            # False, writes to user/Scripts/python/log.txt
47cdf0e10cSrcweirENABLE_EDIT_DIALOG=False                    # offers a minimal editor for editing.
48cdf0e10cSrcweir#-------------------------------------------------------------------
49cdf0e10cSrcweir
50cdf0e10cSrcweirdef encfile(uni):
51cdf0e10cSrcweir    return uni.encode( sys.getfilesystemencoding())
52cdf0e10cSrcweir
53cdf0e10cSrcweirdef lastException2String():
54cdf0e10cSrcweir    (excType,excInstance,excTraceback) = sys.exc_info()
55cdf0e10cSrcweir    ret = str(excType) + ": "+str(excInstance) + "\n" + \
56cdf0e10cSrcweir          uno._uno_extract_printable_stacktrace( excTraceback )
57cdf0e10cSrcweir    return ret
58cdf0e10cSrcweir
59cdf0e10cSrcweirdef logLevel2String( level ):
60cdf0e10cSrcweir    ret = " NONE"
61cdf0e10cSrcweir    if level == LogLevel.ERROR:
62cdf0e10cSrcweir        ret = "ERROR"
63cdf0e10cSrcweir    elif level >= LogLevel.DEBUG:
64cdf0e10cSrcweir        ret = "DEBUG"
65cdf0e10cSrcweir    return ret
66cdf0e10cSrcweir
67cdf0e10cSrcweirdef getLogTarget():
68cdf0e10cSrcweir    ret = sys.stdout
69cdf0e10cSrcweir    if not LOG_STDOUT:
70cdf0e10cSrcweir        try:
71cdf0e10cSrcweir            pathSubst = uno.getComponentContext().ServiceManager.createInstance(
72cdf0e10cSrcweir                "com.sun.star.util.PathSubstitution" )
73cdf0e10cSrcweir            userInstallation =  pathSubst.getSubstituteVariableValue( "user" )
74cdf0e10cSrcweir            if len( userInstallation ) > 0:
75cdf0e10cSrcweir                systemPath = uno.fileUrlToSystemPath( userInstallation + "/Scripts/python/log.txt" )
76cdf0e10cSrcweir                ret = file( systemPath , "a" )
77*15745147SPedro Giffuni        except Exception as e:
78*15745147SPedro Giffuni            print("Exception during creation of pythonscript logfile: "+ lastException2String() + "\n, delagating log to stdout\n")
79cdf0e10cSrcweir    return ret
8013cfd8dfSPedro Giffuni
81cdf0e10cSrcweirclass Logger(LogLevel):
82cdf0e10cSrcweir    def __init__(self , target ):
83cdf0e10cSrcweir        self.target = target
84cdf0e10cSrcweir
85cdf0e10cSrcweir    def isDebugLevel( self ):
86cdf0e10cSrcweir        return self.use >= self.DEBUG
8713cfd8dfSPedro Giffuni
88cdf0e10cSrcweir    def debug( self, msg ):
89cdf0e10cSrcweir        if self.isDebugLevel():
90cdf0e10cSrcweir            self.log( self.DEBUG, msg )
9113cfd8dfSPedro Giffuni
92cdf0e10cSrcweir    def isErrorLevel( self ):
93cdf0e10cSrcweir        return self.use >= self.ERROR
94cdf0e10cSrcweir
95cdf0e10cSrcweir    def error( self, msg ):
96cdf0e10cSrcweir        if self.isErrorLevel():
97cdf0e10cSrcweir            self.log( self.ERROR, msg )
98cdf0e10cSrcweir
99cdf0e10cSrcweir    def log( self, level, msg ):
100cdf0e10cSrcweir        if self.use >= level:
101cdf0e10cSrcweir            try:
102cdf0e10cSrcweir                self.target.write(
103cdf0e10cSrcweir                    time.asctime() +
104cdf0e10cSrcweir                    " [" +
105cdf0e10cSrcweir                    logLevel2String( level ) +
106cdf0e10cSrcweir                    "] " +
107cdf0e10cSrcweir                    encfile(msg) +
108cdf0e10cSrcweir                    "\n" )
109cdf0e10cSrcweir                self.target.flush()
110*15745147SPedro Giffuni            except Exception as e:
111*15745147SPedro Giffuni                print("Error during writing to stdout: " +lastException2String() + "\n")
112cdf0e10cSrcweir
113cdf0e10cSrcweirlog = Logger( getLogTarget() )
114cdf0e10cSrcweir
115cdf0e10cSrcweirlog.debug( "pythonscript loading" )
116cdf0e10cSrcweir
117cdf0e10cSrcweir#from com.sun.star.lang import typeOfXServiceInfo, typeOfXTypeProvider
118cdf0e10cSrcweirfrom com.sun.star.uno import RuntimeException
119cdf0e10cSrcweirfrom com.sun.star.lang import XServiceInfo
120cdf0e10cSrcweirfrom com.sun.star.io import IOException
12161c9e2f8SAriel Constenla-Hailefrom com.sun.star.ucb import CommandAbortedException, XCommandEnvironment, XProgressHandler, Command
122cdf0e10cSrcweirfrom com.sun.star.task import XInteractionHandler
12361c9e2f8SAriel Constenla-Hailefrom com.sun.star.beans import XPropertySet, Property
124cdf0e10cSrcweirfrom com.sun.star.container import XNameContainer
125cdf0e10cSrcweirfrom com.sun.star.xml.sax import XDocumentHandler, InputSource
126cdf0e10cSrcweirfrom com.sun.star.uno import Exception as UnoException
127cdf0e10cSrcweirfrom com.sun.star.script import XInvocation
128cdf0e10cSrcweirfrom com.sun.star.awt import XActionListener
129cdf0e10cSrcweir
130cdf0e10cSrcweirfrom com.sun.star.script.provider import XScriptProvider, XScript, XScriptContext, ScriptFrameworkErrorException
131cdf0e10cSrcweirfrom com.sun.star.script.browse import XBrowseNode
132cdf0e10cSrcweirfrom com.sun.star.script.browse.BrowseNodeTypes import SCRIPT, CONTAINER, ROOT
133cdf0e10cSrcweirfrom com.sun.star.util import XModifyListener
134cdf0e10cSrcweir
135cdf0e10cSrcweirLANGUAGENAME = "Python"
136cdf0e10cSrcweirGLOBAL_SCRIPTCONTEXT_NAME = "XSCRIPTCONTEXT"
137cdf0e10cSrcweirCALLABLE_CONTAINER_NAME =  "g_exportedScripts"
138cdf0e10cSrcweir
139cdf0e10cSrcweir# pythonloader looks for a static g_ImplementationHelper variable
140cdf0e10cSrcweirg_ImplementationHelper = unohelper.ImplementationHelper()
141cdf0e10cSrcweirg_implName = "org.openoffice.pyuno.LanguageScriptProviderFor"+LANGUAGENAME
142cdf0e10cSrcweir
143cdf0e10cSrcweir
144cdf0e10cSrcweir
145cdf0e10cSrcweirBLOCK_SIZE = 65536
146cdf0e10cSrcweirdef readTextFromStream( inputStream ):
147cdf0e10cSrcweir    # read the file
148cdf0e10cSrcweir    code = uno.ByteSequence( "" )
149cdf0e10cSrcweir    while True:
150cdf0e10cSrcweir        read,out = inputStream.readBytes( None , BLOCK_SIZE )
151cdf0e10cSrcweir        code = code + out
152cdf0e10cSrcweir        if read < BLOCK_SIZE:
15313cfd8dfSPedro Giffuni            break
154cdf0e10cSrcweir    return code.value
15513cfd8dfSPedro Giffuni
156cdf0e10cSrcweirdef toIniName( str ):
157cdf0e10cSrcweir    # TODO: what is the official way to get to know whether i am on the windows platform ?
158cdf0e10cSrcweir    if( hasattr(sys , "dllhandle") ):
159cdf0e10cSrcweir        return str + ".ini"
160cdf0e10cSrcweir    return str + "rc"
161cdf0e10cSrcweir
162cdf0e10cSrcweir
163cdf0e10cSrcweir""" definition: storageURI is the system dependent, absolute file url, where the script is stored on disk
164cdf0e10cSrcweir                scriptURI is the system independent uri
165cdf0e10cSrcweir"""
166cdf0e10cSrcweirclass MyUriHelper:
167cdf0e10cSrcweir
168cdf0e10cSrcweir    def __init__( self, ctx, location ):
169cdf0e10cSrcweir        self.s_UriMap = \
170cdf0e10cSrcweir        { "share" : "vnd.sun.star.expand:${$BRAND_BASE_DIR/program/" +  toIniName( "bootstrap") + "::BaseInstallation}/share/Scripts/python" , \
171cdf0e10cSrcweir          "share:uno_packages" : "vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE/uno_packages", \
172cdf0e10cSrcweir          "user" : "vnd.sun.star.expand:${$BRAND_BASE_DIR/program/" + toIniName( "bootstrap") + "::UserInstallation}/user/Scripts/python" , \
17313cfd8dfSPedro Giffuni          "user:uno_packages" : "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/uno_packages" }
174cdf0e10cSrcweir        self.m_uriRefFac = ctx.ServiceManager.createInstanceWithContext("com.sun.star.uri.UriReferenceFactory",ctx)
175cdf0e10cSrcweir        if location.startswith( "vnd.sun.star.tdoc" ):
176cdf0e10cSrcweir            self.m_baseUri = location + "/Scripts/python"
177cdf0e10cSrcweir            self.m_scriptUriLocation = "document"
178cdf0e10cSrcweir        else:
179cdf0e10cSrcweir            self.m_baseUri = expandUri( self.s_UriMap[location] )
180cdf0e10cSrcweir            self.m_scriptUriLocation = location
181735dd73bSPedro Giffuni        log.debug( "initialized urihelper with baseUri="+self.m_baseUri + ",m_scriptUriLocation="+self.m_scriptUriLocation )
18213cfd8dfSPedro Giffuni
183cdf0e10cSrcweir    def getRootStorageURI( self ):
184cdf0e10cSrcweir        return self.m_baseUri
18513cfd8dfSPedro Giffuni
186cdf0e10cSrcweir    def getStorageURI( self, scriptURI ):
187cdf0e10cSrcweir        return self.scriptURI2StorageUri(scriptURI)
188cdf0e10cSrcweir
189cdf0e10cSrcweir    def getScriptURI( self, storageURI ):
190cdf0e10cSrcweir        return self.storageURI2ScriptUri(storageURI)
191cdf0e10cSrcweir
192cdf0e10cSrcweir    def storageURI2ScriptUri( self, storageURI ):
193cdf0e10cSrcweir        if not storageURI.startswith( self.m_baseUri ):
194cdf0e10cSrcweir            message = "pythonscript: storage uri '" + storageURI + "' not in base uri '" + self.m_baseUri + "'"
195735dd73bSPedro Giffuni            log.debug( message )
196cdf0e10cSrcweir            raise RuntimeException( message )
197cdf0e10cSrcweir
198cdf0e10cSrcweir        ret = "vnd.sun.star.script:" + \
199cdf0e10cSrcweir              storageURI[len(self.m_baseUri)+1:].replace("/","|") + \
200cdf0e10cSrcweir              "?language=" + LANGUAGENAME + "&location=" + self.m_scriptUriLocation
201735dd73bSPedro Giffuni        log.debug( "converting storageURI="+storageURI + " to scriptURI=" + ret )
202cdf0e10cSrcweir        return ret
20313cfd8dfSPedro Giffuni
204cdf0e10cSrcweir    def scriptURI2StorageUri( self, scriptURI ):
205cdf0e10cSrcweir        try:
206cdf0e10cSrcweir            myUri = self.m_uriRefFac.parse(scriptURI)
207cdf0e10cSrcweir            ret = self.m_baseUri + "/" + myUri.getName().replace( "|", "/" )
208735dd73bSPedro Giffuni            log.debug( "converting scriptURI="+scriptURI + " to storageURI=" + ret )
209cdf0e10cSrcweir            return ret
210*15745147SPedro Giffuni        except UnoException as e:
211cdf0e10cSrcweir            log.error( "error during converting scriptURI="+scriptURI + ": " + e.Message)
212cdf0e10cSrcweir            raise RuntimeException( "pythonscript:scriptURI2StorageUri: " +e.getMessage(), None )
213*15745147SPedro Giffuni        except Exception as e:
214cdf0e10cSrcweir            log.error( "error during converting scriptURI="+scriptURI + ": " + str(e))
215cdf0e10cSrcweir            raise RuntimeException( "pythonscript:scriptURI2StorageUri: " + str(e), None )
21613cfd8dfSPedro Giffuni
217cdf0e10cSrcweir
218cdf0e10cSrcweirclass ModuleEntry:
219cdf0e10cSrcweir    def __init__( self, lastRead, module ):
220cdf0e10cSrcweir        self.lastRead = lastRead
221cdf0e10cSrcweir        self.module = module
222cdf0e10cSrcweir
223cdf0e10cSrcweirdef hasChanged( oldDate, newDate ):
224cdf0e10cSrcweir    return newDate.Year > oldDate.Year or \
225cdf0e10cSrcweir           newDate.Month > oldDate.Month or \
226cdf0e10cSrcweir           newDate.Day > oldDate.Day or \
227cdf0e10cSrcweir           newDate.Hours > oldDate.Hours or \
228cdf0e10cSrcweir           newDate.Minutes > oldDate.Minutes or \
229cdf0e10cSrcweir           newDate.Seconds > oldDate.Seconds or \
230cdf0e10cSrcweir           newDate.HundredthSeconds > oldDate.HundredthSeconds
231cdf0e10cSrcweir
232cdf0e10cSrcweirdef ensureSourceState( code ):
233cdf0e10cSrcweir    if not code.endswith( "\n" ):
234cdf0e10cSrcweir        code = code + "\n"
235cdf0e10cSrcweir    code = code.replace( "\r", "" )
236cdf0e10cSrcweir    return code
237cdf0e10cSrcweir
238cdf0e10cSrcweir
239cdf0e10cSrcweirdef checkForPythonPathBesideScript( url ):
240cdf0e10cSrcweir    if url.startswith( "file:" ):
241cdf0e10cSrcweir        path = unohelper.fileUrlToSystemPath( url+"/pythonpath.zip" );
242cdf0e10cSrcweir        log.log( LogLevel.DEBUG,  "checking for existence of " + path )
243cdf0e10cSrcweir        if 1 == os.access( encfile(path), os.F_OK) and not path in sys.path:
244cdf0e10cSrcweir            log.log( LogLevel.DEBUG, "adding " + path + " to sys.path" )
245cdf0e10cSrcweir            sys.path.append( path )
246cdf0e10cSrcweir
247cdf0e10cSrcweir        path = unohelper.fileUrlToSystemPath( url+"/pythonpath" );
248cdf0e10cSrcweir        log.log( LogLevel.DEBUG,  "checking for existence of " + path )
249cdf0e10cSrcweir        if 1 == os.access( encfile(path), os.F_OK) and not path in sys.path:
250cdf0e10cSrcweir            log.log( LogLevel.DEBUG, "adding " + path + " to sys.path" )
251cdf0e10cSrcweir            sys.path.append( path )
25213cfd8dfSPedro Giffuni
25313cfd8dfSPedro Giffuni
254cdf0e10cSrcweirclass ScriptContext(unohelper.Base):
25561c9e2f8SAriel Constenla-Haile    def __init__( self, ctx, doc, inv ):
256cdf0e10cSrcweir        self.ctx = ctx
257cdf0e10cSrcweir        self.doc = doc
25861c9e2f8SAriel Constenla-Haile        self.inv = inv
25913cfd8dfSPedro Giffuni
260cdf0e10cSrcweir   # XScriptContext
261cdf0e10cSrcweir    def getDocument(self):
26261c9e2f8SAriel Constenla-Haile        if self.doc:
26361c9e2f8SAriel Constenla-Haile            return self.doc
264cdf0e10cSrcweir        return self.getDesktop().getCurrentComponent()
265cdf0e10cSrcweir
266cdf0e10cSrcweir    def getDesktop(self):
267cdf0e10cSrcweir        return self.ctx.ServiceManager.createInstanceWithContext(
268cdf0e10cSrcweir            "com.sun.star.frame.Desktop", self.ctx )
269cdf0e10cSrcweir
270cdf0e10cSrcweir    def getComponentContext(self):
271cdf0e10cSrcweir        return self.ctx
272cdf0e10cSrcweir
27361c9e2f8SAriel Constenla-Haile    def getInvocationContext(self):
27461c9e2f8SAriel Constenla-Haile        return self.inv
27561c9e2f8SAriel Constenla-Haile
276cdf0e10cSrcweir#----------------------------------
277cdf0e10cSrcweir# Global Module Administration
278cdf0e10cSrcweir# does not fit together with script
279cdf0e10cSrcweir# engine lifetime management
280cdf0e10cSrcweir#----------------------------------
281cdf0e10cSrcweir#g_scriptContext = ScriptContext( uno.getComponentContext(), None )
282cdf0e10cSrcweir#g_modules = {}
283cdf0e10cSrcweir#def getModuleByUrl( url, sfa ):
284cdf0e10cSrcweir#    entry =  g_modules.get(url)
285cdf0e10cSrcweir#    load = True
286cdf0e10cSrcweir#    lastRead = sfa.getDateTimeModified( url )
287cdf0e10cSrcweir#    if entry:
288cdf0e10cSrcweir#        if hasChanged( entry.lastRead, lastRead ):
289735dd73bSPedro Giffuni#            log.debug("file " + url + " has changed, reloading")
290cdf0e10cSrcweir#        else:
291cdf0e10cSrcweir#            load = False
29213cfd8dfSPedro Giffuni#
293cdf0e10cSrcweir#    if load:
294735dd73bSPedro Giffuni#        log.debug( "opening >" + url + "<" )
295cdf0e10cSrcweir#
296cdf0e10cSrcweir#        code = readTextFromStream( sfa.openFileRead( url ) )
29713cfd8dfSPedro Giffuni
298cdf0e10cSrcweir        # execute the module
299cdf0e10cSrcweir#        entry = ModuleEntry( lastRead, imp.new_module("ooo_script_framework") )
300cdf0e10cSrcweir#        entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = g_scriptContext
301cdf0e10cSrcweir#        entry.module.__file__ = url
302cdf0e10cSrcweir#        exec code in entry.module.__dict__
303cdf0e10cSrcweir#        g_modules[ url ] = entry
304735dd73bSPedro Giffuni#        log.debug( "mapped " + url + " to " + str( entry.module ) )
305cdf0e10cSrcweir#    return entry.module
306cdf0e10cSrcweir
307cdf0e10cSrcweirclass ProviderContext:
308cdf0e10cSrcweir    def __init__( self, storageType, sfa, uriHelper, scriptContext ):
309cdf0e10cSrcweir        self.storageType = storageType
310cdf0e10cSrcweir        self.sfa = sfa
311cdf0e10cSrcweir        self.uriHelper = uriHelper
312cdf0e10cSrcweir        self.scriptContext = scriptContext
313cdf0e10cSrcweir        self.modules = {}
314cdf0e10cSrcweir        self.rootUrl = None
315cdf0e10cSrcweir        self.mapPackageName2Path = None
316cdf0e10cSrcweir
317cdf0e10cSrcweir    def getTransientPartFromUrl( self, url ):
318cdf0e10cSrcweir        rest = url.replace( self.rootUrl , "",1 ).replace( "/","",1)
319cdf0e10cSrcweir        return rest[0:rest.find("/")]
32013cfd8dfSPedro Giffuni
321cdf0e10cSrcweir    def getPackageNameFromUrl( self, url ):
322cdf0e10cSrcweir        rest = url.replace( self.rootUrl , "",1 ).replace( "/","",1)
323cdf0e10cSrcweir        start = rest.find("/") +1
324cdf0e10cSrcweir        return rest[start:rest.find("/",start)]
32513cfd8dfSPedro Giffuni
32613cfd8dfSPedro Giffuni
327cdf0e10cSrcweir    def removePackageByUrl( self, url ):
328*15745147SPedro Giffuni        items = list(self.mapPackageName2Path.items())
329cdf0e10cSrcweir        for i in items:
330cdf0e10cSrcweir            if url in i[1].pathes:
331cdf0e10cSrcweir                self.mapPackageName2Path.pop(i[0])
332cdf0e10cSrcweir                break
333cdf0e10cSrcweir
334cdf0e10cSrcweir    def addPackageByUrl( self, url ):
335cdf0e10cSrcweir        packageName = self.getPackageNameFromUrl( url )
336cdf0e10cSrcweir        transientPart = self.getTransientPartFromUrl( url )
337735dd73bSPedro Giffuni        log.debug( "addPackageByUrl : " + packageName + ", " + transientPart + "("+url+")" + ", rootUrl="+self.rootUrl )
338*15745147SPedro Giffuni        if packageName in self.mapPackageName2Path:
339cdf0e10cSrcweir            package = self.mapPackageName2Path[ packageName ]
340cdf0e10cSrcweir            package.pathes = package.pathes + (url, )
341cdf0e10cSrcweir        else:
342cdf0e10cSrcweir            package = Package( (url,), transientPart)
343cdf0e10cSrcweir            self.mapPackageName2Path[ packageName ] = package
34413cfd8dfSPedro Giffuni
345cdf0e10cSrcweir    def isUrlInPackage( self, url ):
346*15745147SPedro Giffuni        values = list(self.mapPackageName2Path.values())
347cdf0e10cSrcweir        for i in values:
34813cfd8dfSPedro Giffuni#           print "checking " + url + " in " + str(i.pathes)
349cdf0e10cSrcweir            if url in i.pathes:
35013cfd8dfSPedro Giffuni                return True
351cdf0e10cSrcweir#        print "false"
352cdf0e10cSrcweir        return False
35313cfd8dfSPedro Giffuni
354cdf0e10cSrcweir    def setPackageAttributes( self, mapPackageName2Path, rootUrl ):
355cdf0e10cSrcweir        self.mapPackageName2Path = mapPackageName2Path
356cdf0e10cSrcweir        self.rootUrl = rootUrl
35713cfd8dfSPedro Giffuni
358cdf0e10cSrcweir    def getPersistentUrlFromStorageUrl( self, url ):
359cdf0e10cSrcweir        # package name is the second directory
360cdf0e10cSrcweir        ret = url
361cdf0e10cSrcweir        if self.rootUrl:
362cdf0e10cSrcweir            pos = len( self.rootUrl) +1
363cdf0e10cSrcweir            ret = url[0:pos]+url[url.find("/",pos)+1:len(url)]
364735dd73bSPedro Giffuni        log.debug( "getPersistentUrlFromStorageUrl " + url +  " -> "+ ret)
365cdf0e10cSrcweir        return ret
366cdf0e10cSrcweir
367cdf0e10cSrcweir    def getStorageUrlFromPersistentUrl( self, url):
368cdf0e10cSrcweir        ret = url
369cdf0e10cSrcweir        if self.rootUrl:
370cdf0e10cSrcweir            pos = len(self.rootUrl)+1
371cdf0e10cSrcweir            packageName = url[pos:url.find("/",pos+1)]
372cdf0e10cSrcweir            package = self.mapPackageName2Path[ packageName ]
373cdf0e10cSrcweir            ret = url[0:pos]+ package.transientPathElement + "/" + url[pos:len(url)]
374735dd73bSPedro Giffuni        log.debug( "getStorageUrlFromPersistentUrl " + url + " -> "+ ret)
375cdf0e10cSrcweir        return ret
376cdf0e10cSrcweir
377cdf0e10cSrcweir    def getFuncsByUrl( self, url ):
378cdf0e10cSrcweir        src = readTextFromStream( self.sfa.openFileRead( url ) )
379cdf0e10cSrcweir        checkForPythonPathBesideScript( url[0:url.rfind('/')] )
380cdf0e10cSrcweir        src = ensureSourceState( src )
381cdf0e10cSrcweir
382cdf0e10cSrcweir        allFuncs = []
383cdf0e10cSrcweir        g_exportedScripts = []
384cdf0e10cSrcweir
38513cfd8dfSPedro Giffuni        a = ast.parse(src, url)
38613cfd8dfSPedro Giffuni
38713cfd8dfSPedro Giffuni        if isinstance(a, ast.Module):
38813cfd8dfSPedro Giffuni            for node in a.body:
38913cfd8dfSPedro Giffuni                if isinstance(node, ast.FunctionDef):
39013cfd8dfSPedro Giffuni                    allFuncs.append(node.name)
39113cfd8dfSPedro Giffuni                elif isinstance(node, ast.Assign):
39213cfd8dfSPedro Giffuni                    is_exported = False
39313cfd8dfSPedro Giffuni                    for subnode in node.targets:
39413cfd8dfSPedro Giffuni                        if isinstance(subnode, ast.Name) and \
39513cfd8dfSPedro Giffuni                            subnode.id == "g_exportedScripts":
39613cfd8dfSPedro Giffuni                            is_exported = True
39713cfd8dfSPedro Giffuni                            break
39813cfd8dfSPedro Giffuni                    if is_exported:
39913cfd8dfSPedro Giffuni                        value_node = node.value
40013cfd8dfSPedro Giffuni                        if isinstance(value_node, ast.List) or \
40113cfd8dfSPedro Giffuni                            isinstance(value_node, ast.Tuple):
40213cfd8dfSPedro Giffuni                            for elt in value_node.elts:
40313cfd8dfSPedro Giffuni                                if isinstance(elt, ast.Str):
40413cfd8dfSPedro Giffuni                                    g_exportedScripts.append(elt.s)
40513cfd8dfSPedro Giffuni                                elif isinstance(elt, ast.Name):
40613cfd8dfSPedro Giffuni                                    g_exportedScripts.append(elt.id)
40713cfd8dfSPedro Giffuni                        elif isinstance(value_node, ast.Str):
40813cfd8dfSPedro Giffuni                            g_exportedScripts.append(value_node.s)
40913cfd8dfSPedro Giffuni                        elif isinstance(value_node, ast.Name):
41013cfd8dfSPedro Giffuni                            g_exportedScripts.append(value_node.id)
41113cfd8dfSPedro Giffuni                        return g_exportedScripts
412cdf0e10cSrcweir        return allFuncs
41313cfd8dfSPedro Giffuni
414cdf0e10cSrcweir    def getModuleByUrl( self, url ):
415cdf0e10cSrcweir        entry =  self.modules.get(url)
416cdf0e10cSrcweir        load = True
417cdf0e10cSrcweir        lastRead = self.sfa.getDateTimeModified( url )
418cdf0e10cSrcweir        if entry:
419cdf0e10cSrcweir            if hasChanged( entry.lastRead, lastRead ):
420735dd73bSPedro Giffuni                log.debug( "file " + url + " has changed, reloading" )
421cdf0e10cSrcweir            else:
422cdf0e10cSrcweir                load = False
42313cfd8dfSPedro Giffuni
424cdf0e10cSrcweir        if load:
425735dd73bSPedro Giffuni            log.debug( "opening >" + url + "<" )
42613cfd8dfSPedro Giffuni
427cdf0e10cSrcweir            src = readTextFromStream( self.sfa.openFileRead( url ) )
428cdf0e10cSrcweir            checkForPythonPathBesideScript( url[0:url.rfind('/')] )
42913cfd8dfSPedro Giffuni            src = ensureSourceState( src )
43013cfd8dfSPedro Giffuni
431cdf0e10cSrcweir            # execute the module
432cdf0e10cSrcweir            entry = ModuleEntry( lastRead, imp.new_module("ooo_script_framework") )
433cdf0e10cSrcweir            entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.scriptContext
434cdf0e10cSrcweir
435cdf0e10cSrcweir            code = None
436cdf0e10cSrcweir            if url.startswith( "file:" ):
437cdf0e10cSrcweir                code = compile( src, encfile(uno.fileUrlToSystemPath( url ) ), "exec" )
438cdf0e10cSrcweir            else:
439cdf0e10cSrcweir                code = compile( src, url, "exec" )
440*15745147SPedro Giffuni            exec(code, entry.module.__dict__)
441cdf0e10cSrcweir            entry.module.__file__ = url
442cdf0e10cSrcweir            self.modules[ url ] = entry
443735dd73bSPedro Giffuni            log.debug( "mapped " + url + " to " + str( entry.module ) )
444cdf0e10cSrcweir        return  entry.module
44513cfd8dfSPedro Giffuni
446cdf0e10cSrcweir#--------------------------------------------------
447cdf0e10cSrcweirdef isScript( candidate ):
448cdf0e10cSrcweir    ret = False
449cdf0e10cSrcweir    if isinstance( candidate, type(isScript) ):
450cdf0e10cSrcweir        ret = True
451cdf0e10cSrcweir    return ret
45213cfd8dfSPedro Giffuni
453cdf0e10cSrcweir#-------------------------------------------------------
454cdf0e10cSrcweirclass ScriptBrowseNode( unohelper.Base, XBrowseNode , XPropertySet, XInvocation, XActionListener ):
455cdf0e10cSrcweir    def __init__( self, provCtx, uri, fileName, funcName ):
456cdf0e10cSrcweir        self.fileName = fileName
457cdf0e10cSrcweir        self.funcName = funcName
458cdf0e10cSrcweir        self.provCtx = provCtx
459cdf0e10cSrcweir        self.uri = uri
46013cfd8dfSPedro Giffuni
461cdf0e10cSrcweir    def getName( self ):
462cdf0e10cSrcweir        return self.funcName
463cdf0e10cSrcweir
464cdf0e10cSrcweir    def getChildNodes(self):
465cdf0e10cSrcweir        return ()
466cdf0e10cSrcweir
467cdf0e10cSrcweir    def hasChildNodes(self):
468cdf0e10cSrcweir        return False
46913cfd8dfSPedro Giffuni
470cdf0e10cSrcweir    def getType( self):
471cdf0e10cSrcweir        return SCRIPT
472cdf0e10cSrcweir
473cdf0e10cSrcweir    def getPropertyValue( self, name ):
474cdf0e10cSrcweir        ret = None
475cdf0e10cSrcweir        try:
476cdf0e10cSrcweir            if name == "URI":
477cdf0e10cSrcweir                ret = self.provCtx.uriHelper.getScriptURI(
478cdf0e10cSrcweir                    self.provCtx.getPersistentUrlFromStorageUrl( self.uri + "$" + self.funcName ) )
479cdf0e10cSrcweir            elif name == "Editable" and ENABLE_EDIT_DIALOG:
480cdf0e10cSrcweir                ret = not self.provCtx.sfa.isReadOnly( self.uri )
48113cfd8dfSPedro Giffuni
482735dd73bSPedro Giffuni            log.debug( "ScriptBrowseNode.getPropertyValue called for " + name + ", returning " + str(ret) )
483*15745147SPedro Giffuni        except Exception as e:
484cdf0e10cSrcweir            log.error( "ScriptBrowseNode.getPropertyValue error " + lastException2String())
485cdf0e10cSrcweir            raise
48613cfd8dfSPedro Giffuni
487cdf0e10cSrcweir        return ret
488cdf0e10cSrcweir    def setPropertyValue( self, name, value ):
489735dd73bSPedro Giffuni        log.debug( "ScriptBrowseNode.setPropertyValue called " + name + "=" +str(value ) )
490cdf0e10cSrcweir    def getPropertySetInfo( self ):
491735dd73bSPedro Giffuni        log.debug( "ScriptBrowseNode.getPropertySetInfo called "  )
492cdf0e10cSrcweir        return None
49313cfd8dfSPedro Giffuni
494cdf0e10cSrcweir    def getIntrospection( self ):
495cdf0e10cSrcweir        return None
496cdf0e10cSrcweir
497cdf0e10cSrcweir    def invoke( self, name, params, outparamindex, outparams ):
498cdf0e10cSrcweir        if name == "Editable":
499cdf0e10cSrcweir            servicename = "com.sun.star.awt.DialogProvider"
500cdf0e10cSrcweir            ctx = self.provCtx.scriptContext.getComponentContext()
501cdf0e10cSrcweir            dlgprov = ctx.ServiceManager.createInstanceWithContext(
502cdf0e10cSrcweir                servicename, ctx )
503cdf0e10cSrcweir
504cdf0e10cSrcweir            self.editor = dlgprov.createDialog(
505cdf0e10cSrcweir                "vnd.sun.star.script:" +
506cdf0e10cSrcweir                "ScriptBindingLibrary.MacroEditor?location=application")
507cdf0e10cSrcweir
508cdf0e10cSrcweir            code = readTextFromStream(self.provCtx.sfa.openFileRead(self.uri))
509cdf0e10cSrcweir            code = ensureSourceState( code )
510cdf0e10cSrcweir            self.editor.getControl("EditorTextField").setText(code)
511cdf0e10cSrcweir
512cdf0e10cSrcweir            self.editor.getControl("RunButton").setActionCommand("Run")
513cdf0e10cSrcweir            self.editor.getControl("RunButton").addActionListener(self)
514cdf0e10cSrcweir            self.editor.getControl("SaveButton").setActionCommand("Save")
515cdf0e10cSrcweir            self.editor.getControl("SaveButton").addActionListener(self)
516cdf0e10cSrcweir
517cdf0e10cSrcweir            self.editor.execute()
518cdf0e10cSrcweir
519cdf0e10cSrcweir        return None
520cdf0e10cSrcweir
521cdf0e10cSrcweir    def actionPerformed( self, event ):
522cdf0e10cSrcweir        try:
523cdf0e10cSrcweir            if event.ActionCommand == "Run":
524cdf0e10cSrcweir                code = self.editor.getControl("EditorTextField").getText()
525cdf0e10cSrcweir                code = ensureSourceState( code )
526cdf0e10cSrcweir                mod = imp.new_module("ooo_script_framework")
527cdf0e10cSrcweir                mod.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.provCtx.scriptContext
528*15745147SPedro Giffuni                exec(code, mod.__dict__)
529cdf0e10cSrcweir                values = mod.__dict__.get( CALLABLE_CONTAINER_NAME , None )
530cdf0e10cSrcweir                if not values:
531*15745147SPedro Giffuni                    values = list(mod.__dict__.values())
53213cfd8dfSPedro Giffuni
533cdf0e10cSrcweir                for i in values:
534cdf0e10cSrcweir                    if isScript( i ):
535cdf0e10cSrcweir                        i()
536cdf0e10cSrcweir                        break
53713cfd8dfSPedro Giffuni
538cdf0e10cSrcweir            elif event.ActionCommand == "Save":
539cdf0e10cSrcweir                toWrite = uno.ByteSequence(
540cdf0e10cSrcweir                    str(
541cdf0e10cSrcweir                    self.editor.getControl("EditorTextField").getText().encode(
542cdf0e10cSrcweir                    sys.getdefaultencoding())) )
543cdf0e10cSrcweir                copyUrl = self.uri + ".orig"
544cdf0e10cSrcweir                self.provCtx.sfa.move( self.uri, copyUrl )
545cdf0e10cSrcweir                out = self.provCtx.sfa.openFileWrite( self.uri )
546cdf0e10cSrcweir                out.writeBytes( toWrite )
547cdf0e10cSrcweir                out.close()
548cdf0e10cSrcweir                self.provCtx.sfa.kill( copyUrl )
549735dd73bSPedro Giffuni#                log.debug("Save is not implemented yet")
550cdf0e10cSrcweir#                text = self.editor.getControl("EditorTextField").getText()
551735dd73bSPedro Giffuni#                log.debug("Would save: " + text)
552*15745147SPedro Giffuni        except Exception as e:
553cdf0e10cSrcweir            # TODO: add an error box here !
554cdf0e10cSrcweir            log.error( lastException2String() )
55513cfd8dfSPedro Giffuni
556cdf0e10cSrcweir
557cdf0e10cSrcweir    def setValue( self, name, value ):
558cdf0e10cSrcweir        return None
559cdf0e10cSrcweir
560cdf0e10cSrcweir    def getValue( self, name ):
561cdf0e10cSrcweir        return None
562cdf0e10cSrcweir
563cdf0e10cSrcweir    def hasMethod( self, name ):
564cdf0e10cSrcweir        return False
565cdf0e10cSrcweir
566cdf0e10cSrcweir    def hasProperty( self, name ):
567cdf0e10cSrcweir        return False
568cdf0e10cSrcweir
56913cfd8dfSPedro Giffuni
570cdf0e10cSrcweir#-------------------------------------------------------
571cdf0e10cSrcweirclass FileBrowseNode( unohelper.Base, XBrowseNode ):
572cdf0e10cSrcweir    def __init__( self, provCtx, uri , name ):
573cdf0e10cSrcweir        self.provCtx = provCtx
574cdf0e10cSrcweir        self.uri = uri
575cdf0e10cSrcweir        self.name = name
576cdf0e10cSrcweir        self.funcnames = None
57713cfd8dfSPedro Giffuni
578cdf0e10cSrcweir    def getName( self ):
579cdf0e10cSrcweir        return self.name
58013cfd8dfSPedro Giffuni
581cdf0e10cSrcweir    def getChildNodes(self):
582cdf0e10cSrcweir        ret = ()
583cdf0e10cSrcweir        try:
584cdf0e10cSrcweir            self.funcnames = self.provCtx.getFuncsByUrl( self.uri )
58513cfd8dfSPedro Giffuni
586cdf0e10cSrcweir            scriptNodeList = []
587cdf0e10cSrcweir            for i in self.funcnames:
588cdf0e10cSrcweir                scriptNodeList.append(
589cdf0e10cSrcweir                    ScriptBrowseNode(
590cdf0e10cSrcweir                    self.provCtx, self.uri, self.name, i ))
591cdf0e10cSrcweir            ret = tuple( scriptNodeList )
592735dd73bSPedro Giffuni            log.debug( "returning " +str(len(ret)) + " ScriptChildNodes on " + self.uri )
593*15745147SPedro Giffuni        except Exception as e:
594cdf0e10cSrcweir            text = lastException2String()
595cdf0e10cSrcweir            log.error( "Error while evaluating " + self.uri + ":" + text )
596cdf0e10cSrcweir            raise
597cdf0e10cSrcweir        return ret
598cdf0e10cSrcweir
599cdf0e10cSrcweir    def hasChildNodes(self):
600cdf0e10cSrcweir        try:
601cdf0e10cSrcweir            return len(self.getChildNodes()) > 0
602*15745147SPedro Giffuni        except Exception as e:
603cdf0e10cSrcweir            return False
60413cfd8dfSPedro Giffuni
605cdf0e10cSrcweir    def getType( self):
606cdf0e10cSrcweir        return CONTAINER
607cdf0e10cSrcweir
60813cfd8dfSPedro Giffuni
609cdf0e10cSrcweir
610cdf0e10cSrcweirclass DirBrowseNode( unohelper.Base, XBrowseNode ):
611cdf0e10cSrcweir    def __init__( self, provCtx, name, rootUrl ):
612cdf0e10cSrcweir        self.provCtx = provCtx
613cdf0e10cSrcweir        self.name = name
614cdf0e10cSrcweir        self.rootUrl = rootUrl
615cdf0e10cSrcweir
616cdf0e10cSrcweir    def getName( self ):
617cdf0e10cSrcweir        return self.name
618cdf0e10cSrcweir
619cdf0e10cSrcweir    def getChildNodes( self ):
620cdf0e10cSrcweir        try:
621735dd73bSPedro Giffuni            log.debug( "DirBrowseNode.getChildNodes called for " + self.rootUrl )
622cdf0e10cSrcweir            contents = self.provCtx.sfa.getFolderContents( self.rootUrl, True )
623cdf0e10cSrcweir            browseNodeList = []
624cdf0e10cSrcweir            for i in contents:
625cdf0e10cSrcweir                if i.endswith( ".py" ):
626735dd73bSPedro Giffuni                    log.debug( "adding filenode " + i )
627cdf0e10cSrcweir                    browseNodeList.append(
628cdf0e10cSrcweir                        FileBrowseNode( self.provCtx, i, i[i.rfind("/")+1:len(i)-3] ) )
629cdf0e10cSrcweir                elif self.provCtx.sfa.isFolder( i ) and not i.endswith("/pythonpath"):
630735dd73bSPedro Giffuni                    log.debug( "adding DirBrowseNode " + i )
631cdf0e10cSrcweir                    browseNodeList.append( DirBrowseNode( self.provCtx, i[i.rfind("/")+1:len(i)],i))
632cdf0e10cSrcweir            return tuple( browseNodeList )
633*15745147SPedro Giffuni        except Exception as e:
634cdf0e10cSrcweir            text = lastException2String()
635cdf0e10cSrcweir            log.error( "DirBrowseNode error: " + str(e) + " while evaluating " + self.rootUrl)
636cdf0e10cSrcweir            log.error( text)
637cdf0e10cSrcweir            return ()
638cdf0e10cSrcweir
639cdf0e10cSrcweir    def hasChildNodes( self ):
640cdf0e10cSrcweir        return True
641cdf0e10cSrcweir
642cdf0e10cSrcweir    def getType( self ):
643cdf0e10cSrcweir        return CONTAINER
644cdf0e10cSrcweir
645cdf0e10cSrcweir    def getScript( self, uri ):
646cdf0e10cSrcweir        log.debug( "DirBrowseNode getScript " + uri + " invoked" )
647cdf0e10cSrcweir        raise IllegalArgumentException( "DirBrowseNode couldn't instantiate script " + uri , self , 0 )
648cdf0e10cSrcweir
649cdf0e10cSrcweir
650cdf0e10cSrcweirclass ManifestHandler( XDocumentHandler, unohelper.Base ):
651cdf0e10cSrcweir    def __init__( self, rootUrl ):
652cdf0e10cSrcweir        self.rootUrl = rootUrl
65313cfd8dfSPedro Giffuni
654cdf0e10cSrcweir    def startDocument( self ):
655cdf0e10cSrcweir        self.urlList = []
65613cfd8dfSPedro Giffuni
657cdf0e10cSrcweir    def endDocument( self ):
658cdf0e10cSrcweir        pass
65913cfd8dfSPedro Giffuni
660cdf0e10cSrcweir    def startElement( self , name, attlist):
661cdf0e10cSrcweir        if name == "manifest:file-entry":
662cdf0e10cSrcweir            if attlist.getValueByName( "manifest:media-type" ) == "application/vnd.sun.star.framework-script":
663cdf0e10cSrcweir                self.urlList.append(
664cdf0e10cSrcweir                    self.rootUrl + "/" + attlist.getValueByName( "manifest:full-path" ) )
665cdf0e10cSrcweir
666cdf0e10cSrcweir    def endElement( self, name ):
667cdf0e10cSrcweir        pass
668cdf0e10cSrcweir
669cdf0e10cSrcweir    def characters ( self, chars ):
670cdf0e10cSrcweir        pass
671cdf0e10cSrcweir
672cdf0e10cSrcweir    def ignoreableWhitespace( self, chars ):
673cdf0e10cSrcweir        pass
674cdf0e10cSrcweir
675cdf0e10cSrcweir    def setDocumentLocator( self, locator ):
676cdf0e10cSrcweir        pass
677cdf0e10cSrcweir
678cdf0e10cSrcweirdef isPyFileInPath( sfa, path ):
679cdf0e10cSrcweir    ret = False
680cdf0e10cSrcweir    contents = sfa.getFolderContents( path, True )
681cdf0e10cSrcweir    for i in contents:
682cdf0e10cSrcweir        if sfa.isFolder(i):
683cdf0e10cSrcweir            ret = isPyFileInPath(sfa,i)
684cdf0e10cSrcweir        else:
685cdf0e10cSrcweir            if i.endswith(".py"):
686cdf0e10cSrcweir                ret = True
687cdf0e10cSrcweir        if ret:
688cdf0e10cSrcweir            break
689cdf0e10cSrcweir    return ret
690cdf0e10cSrcweir
69113cfd8dfSPedro Giffuni# extracts META-INF directory from
692cdf0e10cSrcweirdef getPathesFromPackage( rootUrl, sfa ):
693cdf0e10cSrcweir    ret = ()
694cdf0e10cSrcweir    try:
69513cfd8dfSPedro Giffuni        fileUrl = rootUrl + "/META-INF/manifest.xml"
696cdf0e10cSrcweir        inputStream = sfa.openFileRead( fileUrl )
697cdf0e10cSrcweir        parser = uno.getComponentContext().ServiceManager.createInstance( "com.sun.star.xml.sax.Parser" )
698cdf0e10cSrcweir        handler = ManifestHandler( rootUrl )
699cdf0e10cSrcweir        parser.setDocumentHandler( handler )
700cdf0e10cSrcweir        parser.parseStream( InputSource( inputStream , "", fileUrl, fileUrl ) )
701cdf0e10cSrcweir        for i in tuple(handler.urlList):
702cdf0e10cSrcweir            if not isPyFileInPath( sfa, i ):
703cdf0e10cSrcweir                handler.urlList.remove(i)
704cdf0e10cSrcweir        ret = tuple( handler.urlList )
705*15745147SPedro Giffuni    except UnoException as e:
706cdf0e10cSrcweir        text = lastException2String()
707cdf0e10cSrcweir        log.debug( "getPathesFromPackage " + fileUrl + " Exception: " +text )
708cdf0e10cSrcweir        pass
709cdf0e10cSrcweir    return ret
71013cfd8dfSPedro Giffuni
711cdf0e10cSrcweir
712cdf0e10cSrcweirclass Package:
713cdf0e10cSrcweir    def __init__( self, pathes, transientPathElement ):
714cdf0e10cSrcweir        self.pathes = pathes
715cdf0e10cSrcweir        self.transientPathElement = transientPathElement
716cdf0e10cSrcweir
717cdf0e10cSrcweirclass DummyInteractionHandler( unohelper.Base, XInteractionHandler ):
718cdf0e10cSrcweir    def __init__( self ):
719cdf0e10cSrcweir        pass
720cdf0e10cSrcweir    def handle( self, event):
721735dd73bSPedro Giffuni        log.debug( "pythonscript: DummyInteractionHandler.handle " + str( event ) )
722cdf0e10cSrcweir
723cdf0e10cSrcweirclass DummyProgressHandler( unohelper.Base, XProgressHandler ):
724cdf0e10cSrcweir    def __init__( self ):
725cdf0e10cSrcweir        pass
72613cfd8dfSPedro Giffuni
72713cfd8dfSPedro Giffuni    def push( self,status ):
728735dd73bSPedro Giffuni        log.debug( "pythonscript: DummyProgressHandler.push " + str( status ) )
72913cfd8dfSPedro Giffuni    def update( self,status ):
730735dd73bSPedro Giffuni        log.debug( "pythonscript: DummyProgressHandler.update " + str( status ) )
73113cfd8dfSPedro Giffuni    def pop( self ):
732735dd73bSPedro Giffuni        log.debug( "pythonscript: DummyProgressHandler.push " + str( event ) )
733cdf0e10cSrcweir
734cdf0e10cSrcweirclass CommandEnvironment(unohelper.Base, XCommandEnvironment):
735cdf0e10cSrcweir    def __init__( self ):
736cdf0e10cSrcweir        self.progressHandler = DummyProgressHandler()
737cdf0e10cSrcweir        self.interactionHandler = DummyInteractionHandler()
738cdf0e10cSrcweir    def getInteractionHandler( self ):
739cdf0e10cSrcweir        return self.interactionHandler
740cdf0e10cSrcweir    def getProgressHandler( self ):
741cdf0e10cSrcweir        return self.progressHandler
742cdf0e10cSrcweir
743cdf0e10cSrcweir#maybe useful for debugging purposes
744cdf0e10cSrcweir#class ModifyListener( unohelper.Base, XModifyListener ):
745cdf0e10cSrcweir#    def __init__( self ):
746cdf0e10cSrcweir#        pass
747cdf0e10cSrcweir#    def modified( self, event ):
748735dd73bSPedro Giffuni#        log.debug( "pythonscript: ModifyListener.modified " + str( event ) )
749cdf0e10cSrcweir#    def disposing( self, event ):
750735dd73bSPedro Giffuni#        log.debug( "pythonscript: ModifyListener.disposing " + str( event ) )
75161c9e2f8SAriel Constenla-Haile
75261c9e2f8SAriel Constenla-Hailedef getModelFromDocUrl(ctx, url):
75361c9e2f8SAriel Constenla-Haile    """Get document model from document url."""
75461c9e2f8SAriel Constenla-Haile    doc = None
75561c9e2f8SAriel Constenla-Haile    args = ("Local", "Office")
75661c9e2f8SAriel Constenla-Haile    ucb = ctx.getServiceManager().createInstanceWithArgumentsAndContext(
75761c9e2f8SAriel Constenla-Haile        "com.sun.star.ucb.UniversalContentBroker", args, ctx)
75861c9e2f8SAriel Constenla-Haile    identifier = ucb.createContentIdentifier(url)
75961c9e2f8SAriel Constenla-Haile    content = ucb.queryContent(identifier)
76061c9e2f8SAriel Constenla-Haile    p = Property()
76161c9e2f8SAriel Constenla-Haile    p.Name = "DocumentModel"
76261c9e2f8SAriel Constenla-Haile    p.Handle = -1
76313cfd8dfSPedro Giffuni
76461c9e2f8SAriel Constenla-Haile    c = Command()
76561c9e2f8SAriel Constenla-Haile    c.Handle = -1
76661c9e2f8SAriel Constenla-Haile    c.Name = "getPropertyValues"
76761c9e2f8SAriel Constenla-Haile    c.Argument = uno.Any("[]com.sun.star.beans.Property", (p,))
76813cfd8dfSPedro Giffuni
76961c9e2f8SAriel Constenla-Haile    env = CommandEnvironment()
77061c9e2f8SAriel Constenla-Haile    try:
77161c9e2f8SAriel Constenla-Haile        ret = content.execute(c, 0, env)
77261c9e2f8SAriel Constenla-Haile        doc = ret.getObject(1, None)
773*15745147SPedro Giffuni    except Exception as e:
77461c9e2f8SAriel Constenla-Haile        log.isErrorLevel() and log.error("getModelFromDocUrl: %s" % url)
77561c9e2f8SAriel Constenla-Haile    return doc
77661c9e2f8SAriel Constenla-Haile
777cdf0e10cSrcweirdef mapStorageType2PackageContext( storageType ):
778cdf0e10cSrcweir    ret = storageType
779cdf0e10cSrcweir    if( storageType == "share:uno_packages" ):
780cdf0e10cSrcweir        ret = "shared"
781cdf0e10cSrcweir    if( storageType == "user:uno_packages" ):
782cdf0e10cSrcweir        ret = "user"
783cdf0e10cSrcweir    return ret
784cdf0e10cSrcweir
785cdf0e10cSrcweirdef getPackageName2PathMap( sfa, storageType ):
786cdf0e10cSrcweir    ret = {}
787cdf0e10cSrcweir    packageManagerFactory = uno.getComponentContext().getValueByName(
788cdf0e10cSrcweir        "/singletons/com.sun.star.deployment.thePackageManagerFactory" )
789cdf0e10cSrcweir    packageManager = packageManagerFactory.getPackageManager(
790cdf0e10cSrcweir        mapStorageType2PackageContext(storageType))
791cdf0e10cSrcweir#    packageManager.addModifyListener( ModifyListener() )
792735dd73bSPedro Giffuni    log.debug( "pythonscript: getPackageName2PathMap start getDeployedPackages" )
793cdf0e10cSrcweir    packages = packageManager.getDeployedPackages(
794cdf0e10cSrcweir        packageManager.createAbortChannel(), CommandEnvironment( ) )
795735dd73bSPedro Giffuni    log.debug( "pythonscript: getPackageName2PathMap end getDeployedPackages (" + str(len(packages))+")" )
796cdf0e10cSrcweir
797cdf0e10cSrcweir    for i in packages:
798735dd73bSPedro Giffuni        log.debug( "inspecting package " + i.Name + "("+i.Identifier.Value+")" )
799cdf0e10cSrcweir        transientPathElement = penultimateElement( i.URL )
800cdf0e10cSrcweir        j = expandUri( i.URL )
801cdf0e10cSrcweir        pathes = getPathesFromPackage( j, sfa )
802cdf0e10cSrcweir        if len( pathes ) > 0:
803cdf0e10cSrcweir            # map package name to url, we need this later
804cdf0e10cSrcweir            log.isErrorLevel() and log.error( "adding Package " + transientPathElement + " " + str( pathes ) )
805cdf0e10cSrcweir            ret[ lastElement( j ) ] = Package( pathes, transientPathElement )
806cdf0e10cSrcweir    return ret
807cdf0e10cSrcweir
808cdf0e10cSrcweirdef penultimateElement( aStr ):
809cdf0e10cSrcweir    lastSlash = aStr.rindex("/")
810cdf0e10cSrcweir    penultimateSlash = aStr.rindex("/",0,lastSlash-1)
811cdf0e10cSrcweir    return  aStr[ penultimateSlash+1:lastSlash ]
812cdf0e10cSrcweir
813cdf0e10cSrcweirdef lastElement( aStr):
814cdf0e10cSrcweir    return aStr[ aStr.rfind( "/" )+1:len(aStr)]
815cdf0e10cSrcweir
816cdf0e10cSrcweirclass PackageBrowseNode( unohelper.Base, XBrowseNode ):
817cdf0e10cSrcweir    def __init__( self, provCtx, name, rootUrl ):
818cdf0e10cSrcweir        self.provCtx = provCtx
819cdf0e10cSrcweir        self.name = name
820cdf0e10cSrcweir        self.rootUrl = rootUrl
821cdf0e10cSrcweir
822cdf0e10cSrcweir    def getName( self ):
823cdf0e10cSrcweir        return self.name
824cdf0e10cSrcweir
825cdf0e10cSrcweir    def getChildNodes( self ):
826*15745147SPedro Giffuni        items = list(self.provCtx.mapPackageName2Path.items())
827cdf0e10cSrcweir        browseNodeList = []
828cdf0e10cSrcweir        for i in items:
829cdf0e10cSrcweir            if len( i[1].pathes ) == 1:
830cdf0e10cSrcweir                browseNodeList.append(
831cdf0e10cSrcweir                    DirBrowseNode( self.provCtx, i[0], i[1].pathes[0] ))
832cdf0e10cSrcweir            else:
833cdf0e10cSrcweir                for j in i[1].pathes:
834cdf0e10cSrcweir                    browseNodeList.append(
835cdf0e10cSrcweir                        DirBrowseNode( self.provCtx, i[0]+"."+lastElement(j), j ) )
836cdf0e10cSrcweir        return tuple( browseNodeList )
837cdf0e10cSrcweir
838cdf0e10cSrcweir    def hasChildNodes( self ):
839cdf0e10cSrcweir        return len( self.mapPackageName2Path ) > 0
840cdf0e10cSrcweir
841cdf0e10cSrcweir    def getType( self ):
842cdf0e10cSrcweir        return CONTAINER
843cdf0e10cSrcweir
844cdf0e10cSrcweir    def getScript( self, uri ):
845cdf0e10cSrcweir        log.debug( "DirBrowseNode getScript " + uri + " invoked" )
846cdf0e10cSrcweir        raise IllegalArgumentException( "PackageBrowseNode couldn't instantiate script " + uri , self , 0 )
847cdf0e10cSrcweir
848cdf0e10cSrcweir
849cdf0e10cSrcweir
850cdf0e10cSrcweir
851cdf0e10cSrcweirclass PythonScript( unohelper.Base, XScript ):
852cdf0e10cSrcweir    def __init__( self, func, mod ):
853cdf0e10cSrcweir        self.func = func
854cdf0e10cSrcweir        self.mod = mod
855cdf0e10cSrcweir    def invoke(self, args, out, outindex ):
856735dd73bSPedro Giffuni        log.debug( "PythonScript.invoke " + str( args ) )
857cdf0e10cSrcweir        try:
858cdf0e10cSrcweir            ret = self.func( *args )
859*15745147SPedro Giffuni        except UnoException as e:
860cdf0e10cSrcweir            # UNO Exception continue to fly ...
861cdf0e10cSrcweir            text = lastException2String()
862cdf0e10cSrcweir            complete = "Error during invoking function " + \
863cdf0e10cSrcweir                str(self.func.__name__) + " in module " + \
864cdf0e10cSrcweir                self.mod.__file__ + " (" + text + ")"
865735dd73bSPedro Giffuni            log.debug( complete )
866cdf0e10cSrcweir            # some people may beat me up for modifying the exception text,
867cdf0e10cSrcweir            # but otherwise office just shows
868cdf0e10cSrcweir            # the type name and message text with no more information,
86913cfd8dfSPedro Giffuni            # this is really bad for most users.
870cdf0e10cSrcweir            e.Message = e.Message + " (" + complete + ")"
871cdf0e10cSrcweir            raise
872*15745147SPedro Giffuni        except Exception as e:
873cdf0e10cSrcweir            # General python exception are converted to uno RuntimeException
874cdf0e10cSrcweir            text = lastException2String()
875cdf0e10cSrcweir            complete = "Error during invoking function " + \
876cdf0e10cSrcweir                str(self.func.__name__) + " in module " + \
877cdf0e10cSrcweir                self.mod.__file__ + " (" + text + ")"
878735dd73bSPedro Giffuni            log.debug( complete )
879cdf0e10cSrcweir            raise RuntimeException( complete , self )
880735dd73bSPedro Giffuni        log.debug( "PythonScript.invoke ret = " + str( ret ) )
881cdf0e10cSrcweir        return ret, (), ()
882cdf0e10cSrcweir
883cdf0e10cSrcweirdef expandUri(  uri ):
884cdf0e10cSrcweir    if uri.startswith( "vnd.sun.star.expand:" ):
885cdf0e10cSrcweir        uri = uri.replace( "vnd.sun.star.expand:", "",1)
886cdf0e10cSrcweir        uri = uno.getComponentContext().getByName(
887cdf0e10cSrcweir                    "/singletons/com.sun.star.util.theMacroExpander" ).expandMacros( uri )
888cdf0e10cSrcweir    if uri.startswith( "file:" ):
889cdf0e10cSrcweir        uri = uno.absolutize("",uri)   # necessary to get rid of .. in uri
890cdf0e10cSrcweir    return uri
89113cfd8dfSPedro Giffuni
892cdf0e10cSrcweir#--------------------------------------------------------------
893cdf0e10cSrcweirclass PythonScriptProvider( unohelper.Base, XBrowseNode, XScriptProvider, XNameContainer):
894cdf0e10cSrcweir    def __init__( self, ctx, *args ):
895cdf0e10cSrcweir        if log.isDebugLevel():
896cdf0e10cSrcweir            mystr = ""
897cdf0e10cSrcweir            for i in args:
898cdf0e10cSrcweir                if len(mystr) > 0:
899cdf0e10cSrcweir                    mystr = mystr +","
900cdf0e10cSrcweir                mystr = mystr + str(i)
901cdf0e10cSrcweir            log.debug( "Entering PythonScriptProvider.ctor" + mystr )
902cdf0e10cSrcweir
90361c9e2f8SAriel Constenla-Haile        doc = None
90461c9e2f8SAriel Constenla-Haile        inv = None
905cdf0e10cSrcweir        storageType = ""
90661c9e2f8SAriel Constenla-Haile
907cdf0e10cSrcweir        if isinstance(args[0],unicode ):
908cdf0e10cSrcweir            storageType = args[0]
90961c9e2f8SAriel Constenla-Haile            if storageType.startswith( "vnd.sun.star.tdoc" ):
91061c9e2f8SAriel Constenla-Haile                doc = getModelFromDocUrl(ctx, storageType)
911cdf0e10cSrcweir        else:
91261c9e2f8SAriel Constenla-Haile            inv = args[0]
91361c9e2f8SAriel Constenla-Haile            try:
91461c9e2f8SAriel Constenla-Haile                doc = inv.ScriptContainer
91561c9e2f8SAriel Constenla-Haile                content = ctx.getServiceManager().createInstanceWithContext(
91613cfd8dfSPedro Giffuni                    "com.sun.star.frame.TransientDocumentsDocumentContentFactory",
91761c9e2f8SAriel Constenla-Haile                    ctx).createDocumentContent(doc)
91861c9e2f8SAriel Constenla-Haile                storageType = content.getIdentifier().getContentIdentifier()
919*15745147SPedro Giffuni            except Exception as e:
92061c9e2f8SAriel Constenla-Haile                text = lastException2String()
92161c9e2f8SAriel Constenla-Haile                log.error( text )
92261c9e2f8SAriel Constenla-Haile
923cdf0e10cSrcweir        isPackage = storageType.endswith( ":uno_packages" )
924cdf0e10cSrcweir
925cdf0e10cSrcweir        try:
926cdf0e10cSrcweir#            urlHelper = ctx.ServiceManager.createInstanceWithArgumentsAndContext(
927cdf0e10cSrcweir#                "com.sun.star.script.provider.ScriptURIHelper", (LANGUAGENAME, storageType), ctx)
928cdf0e10cSrcweir            urlHelper = MyUriHelper( ctx, storageType )
929735dd73bSPedro Giffuni            log.debug( "got urlHelper " + str( urlHelper ) )
93013cfd8dfSPedro Giffuni
931cdf0e10cSrcweir            rootUrl = expandUri( urlHelper.getRootStorageURI() )
932735dd73bSPedro Giffuni            log.debug( storageType + " transformed to " + rootUrl )
933cdf0e10cSrcweir
934cdf0e10cSrcweir            ucbService = "com.sun.star.ucb.SimpleFileAccess"
935cdf0e10cSrcweir            sfa = ctx.ServiceManager.createInstanceWithContext( ucbService, ctx )
936cdf0e10cSrcweir            if not sfa:
937cdf0e10cSrcweir                log.debug("PythonScriptProvider couldn't instantiate " +ucbService)
938cdf0e10cSrcweir                raise RuntimeException(
939cdf0e10cSrcweir                    "PythonScriptProvider couldn't instantiate " +ucbService, self)
940cdf0e10cSrcweir            self.provCtx = ProviderContext(
94161c9e2f8SAriel Constenla-Haile                storageType, sfa, urlHelper, ScriptContext( uno.getComponentContext(), doc, inv ) )
942cdf0e10cSrcweir            if isPackage:
943cdf0e10cSrcweir                mapPackageName2Path = getPackageName2PathMap( sfa, storageType )
944cdf0e10cSrcweir                self.provCtx.setPackageAttributes( mapPackageName2Path , rootUrl )
945cdf0e10cSrcweir                self.dirBrowseNode = PackageBrowseNode( self.provCtx, LANGUAGENAME, rootUrl )
946cdf0e10cSrcweir            else:
947cdf0e10cSrcweir                self.dirBrowseNode = DirBrowseNode( self.provCtx, LANGUAGENAME, rootUrl )
94813cfd8dfSPedro Giffuni
949*15745147SPedro Giffuni        except Exception as e:
950cdf0e10cSrcweir            text = lastException2String()
951cdf0e10cSrcweir            log.debug( "PythonScriptProvider could not be instantiated because of : " + text )
952cdf0e10cSrcweir            raise e
953cdf0e10cSrcweir
954cdf0e10cSrcweir    def getName( self ):
955cdf0e10cSrcweir        return self.dirBrowseNode.getName()
956cdf0e10cSrcweir
957cdf0e10cSrcweir    def getChildNodes( self ):
95813cfd8dfSPedro Giffuni        return self.dirBrowseNode.getChildNodes()
959cdf0e10cSrcweir
960cdf0e10cSrcweir    def hasChildNodes( self ):
961cdf0e10cSrcweir        return self.dirBrowseNode.hasChildNodes()
962cdf0e10cSrcweir
963cdf0e10cSrcweir    def getType( self ):
964cdf0e10cSrcweir        return self.dirBrowseNode.getType()
965cdf0e10cSrcweir
966cdf0e10cSrcweir    def getScript( self, uri ):
967cdf0e10cSrcweir        log.debug( "DirBrowseNode getScript " + uri + " invoked" )
96813cfd8dfSPedro Giffuni
969cdf0e10cSrcweir        raise IllegalArgumentException( "DirBrowseNode couldn't instantiate script " + uri , self , 0 )
970cdf0e10cSrcweir
971cdf0e10cSrcweir    def getScript( self, scriptUri ):
972cdf0e10cSrcweir        try:
973735dd73bSPedro Giffuni            log.debug( "getScript " + scriptUri + " invoked")
97413cfd8dfSPedro Giffuni
975cdf0e10cSrcweir            storageUri = self.provCtx.getStorageUrlFromPersistentUrl(
976cdf0e10cSrcweir                self.provCtx.uriHelper.getStorageURI(scriptUri) );
977735dd73bSPedro Giffuni            log.debug( "getScript: storageUri = " + storageUri)
978cdf0e10cSrcweir            fileUri = storageUri[0:storageUri.find( "$" )]
97913cfd8dfSPedro Giffuni            funcName = storageUri[storageUri.find( "$" )+1:len(storageUri)]
98013cfd8dfSPedro Giffuni
981cdf0e10cSrcweir            mod = self.provCtx.getModuleByUrl( fileUri )
982735dd73bSPedro Giffuni            log.debug( " got mod " + str(mod) )
98313cfd8dfSPedro Giffuni
984cdf0e10cSrcweir            func = mod.__dict__[ funcName ]
985cdf0e10cSrcweir
986735dd73bSPedro Giffuni            log.debug( "got func " + str( func ) )
987cdf0e10cSrcweir            return PythonScript( func, mod )
988*15745147SPedro Giffuni        except Exception as e:
989cdf0e10cSrcweir            text = lastException2String()
990cdf0e10cSrcweir            log.error( text )
991cdf0e10cSrcweir            raise ScriptFrameworkErrorException( text, self, scriptUri, LANGUAGENAME, 0 )
99213cfd8dfSPedro Giffuni
993cdf0e10cSrcweir
994cdf0e10cSrcweir    # XServiceInfo
995cdf0e10cSrcweir    def getSupportedServices( self ):
996cdf0e10cSrcweir        return g_ImplementationHelper.getSupportedServices(g_implName)
997cdf0e10cSrcweir
998cdf0e10cSrcweir    def supportsService( self, ServiceName ):
999cdf0e10cSrcweir        return g_ImplementationHelper.supportsService( g_implName, ServiceName )
1000cdf0e10cSrcweir
1001cdf0e10cSrcweir    def getImplementationName(self):
1002cdf0e10cSrcweir        return g_implName
1003cdf0e10cSrcweir
1004cdf0e10cSrcweir    def getByName( self, name ):
1005cdf0e10cSrcweir        log.debug( "getByName called" + str( name ))
1006cdf0e10cSrcweir        return None
1007cdf0e10cSrcweir
100813cfd8dfSPedro Giffuni
1009cdf0e10cSrcweir    def getElementNames( self ):
1010cdf0e10cSrcweir        log.debug( "getElementNames called")
1011cdf0e10cSrcweir        return ()
101213cfd8dfSPedro Giffuni
1013cdf0e10cSrcweir    def hasByName( self, name ):
1014cdf0e10cSrcweir        try:
1015cdf0e10cSrcweir            log.debug( "hasByName called " + str( name ))
1016cdf0e10cSrcweir            uri = expandUri(name)
1017cdf0e10cSrcweir            ret = self.provCtx.isUrlInPackage( uri )
1018cdf0e10cSrcweir            log.debug( "hasByName " + uri + " " +str( ret ) )
1019cdf0e10cSrcweir            return ret
1020*15745147SPedro Giffuni        except Exception as e:
1021cdf0e10cSrcweir            text = lastException2String()
1022cdf0e10cSrcweir            log.debug( "Error in hasByName:" +  text )
1023cdf0e10cSrcweir            return False
1024cdf0e10cSrcweir
1025cdf0e10cSrcweir    def removeByName( self, name ):
1026cdf0e10cSrcweir        log.debug( "removeByName called" + str( name ))
1027cdf0e10cSrcweir        uri = expandUri( name )
1028cdf0e10cSrcweir        if self.provCtx.isUrlInPackage( uri ):
1029cdf0e10cSrcweir            self.provCtx.removePackageByUrl( uri )
1030cdf0e10cSrcweir        else:
1031cdf0e10cSrcweir            log.debug( "removeByName unknown uri " + str( name ) + ", ignoring" )
1032cdf0e10cSrcweir            raise NoSuchElementException( uri + "is not in package" , self )
1033cdf0e10cSrcweir        log.debug( "removeByName called" + str( uri ) + " successful" )
103413cfd8dfSPedro Giffuni
1035cdf0e10cSrcweir    def insertByName( self, name, value ):
1036cdf0e10cSrcweir        log.debug( "insertByName called " + str( name ) + " " + str( value ))
1037cdf0e10cSrcweir        uri = expandUri( name )
1038cdf0e10cSrcweir        if isPyFileInPath( self.provCtx.sfa, uri ):
1039cdf0e10cSrcweir            self.provCtx.addPackageByUrl( uri )
1040cdf0e10cSrcweir        else:
1041cdf0e10cSrcweir            # package is no python package ...
1042cdf0e10cSrcweir            log.debug( "insertByName: no python files in " + str( uri ) + ", ignoring" )
1043cdf0e10cSrcweir            raise IllegalArgumentException( uri + " does not contain .py files", self, 1 )
1044cdf0e10cSrcweir        log.debug( "insertByName called " + str( uri ) + " successful" )
1045cdf0e10cSrcweir
1046cdf0e10cSrcweir    def replaceByName( self, name, value ):
1047cdf0e10cSrcweir        log.debug( "replaceByName called " + str( name ) + " " + str( value ))
1048cdf0e10cSrcweir        removeByName( name )
1049cdf0e10cSrcweir        insertByName( name )
1050cdf0e10cSrcweir        log.debug( "replaceByName called" + str( uri ) + " successful" )
1051cdf0e10cSrcweir
1052cdf0e10cSrcweir    def getElementType( self ):
1053cdf0e10cSrcweir        log.debug( "getElementType called" )
1054cdf0e10cSrcweir        return uno.getTypeByName( "void" )
105513cfd8dfSPedro Giffuni
1056cdf0e10cSrcweir    def hasElements( self ):
1057cdf0e10cSrcweir        log.debug( "hasElements got called")
1058cdf0e10cSrcweir        return False
105913cfd8dfSPedro Giffuni
1060cdf0e10cSrcweirg_ImplementationHelper.addImplementation( \
106113cfd8dfSPedro Giffuni        PythonScriptProvider,g_implName, \
1062cdf0e10cSrcweir    ("com.sun.star.script.provider.LanguageScriptProvider",
1063cdf0e10cSrcweir     "com.sun.star.script.provider.ScriptProviderFor"+ LANGUAGENAME,),)
1064cdf0e10cSrcweir
1065cdf0e10cSrcweir
1066cdf0e10cSrcweirlog.debug( "pythonscript finished intializing" )
1067