home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2011 October / maximum-cd-2011-10.iso / DiscContents / LibO_3.4.1_Win_x86_install_multi.exe / libreoffice1.cab / pythonscript.py < prev    next >
Encoding:
Python Source  |  2011-06-24  |  37.5 KB  |  988 lines

  1. # XScript implementation for python
  2. import uno
  3. import unohelper
  4. import sys
  5. import os
  6. import imp
  7. import time
  8. import compiler
  9.  
  10. class LogLevel:
  11.     NONE = 0
  12.     ERROR = 1
  13.     DEBUG = 2
  14.  
  15. # Configuration ----------------------------------------------------
  16. LogLevel.use = LogLevel.NONE                # production level
  17. #LogLevel.use = LogLevel.ERROR               # for script developers
  18. #LogLevel.use = LogLevel.DEBUG               # for script framework developers
  19. LOG_STDOUT = True                           # True, writes to stdout (difficult on windows)
  20.                                             # False, writes to user/Scripts/python/log.txt
  21. ENABLE_EDIT_DIALOG=False                    # offers a minimal editor for editing.
  22. #-------------------------------------------------------------------
  23.  
  24. def encfile(uni):
  25.     return uni.encode( sys.getfilesystemencoding())
  26.  
  27. def lastException2String():
  28.     (excType,excInstance,excTraceback) = sys.exc_info()
  29.     ret = str(excType) + ": "+str(excInstance) + "\n" + \
  30.           uno._uno_extract_printable_stacktrace( excTraceback )
  31.     return ret
  32.  
  33. def logLevel2String( level ):
  34.     ret = " NONE"
  35.     if level == LogLevel.ERROR:
  36.         ret = "ERROR"
  37.     elif level >= LogLevel.DEBUG:
  38.         ret = "DEBUG"
  39.     return ret
  40.  
  41. def getLogTarget():
  42.     ret = sys.stdout
  43.     if not LOG_STDOUT:
  44.         try:
  45.             pathSubst = uno.getComponentContext().ServiceManager.createInstance(
  46.                 "com.sun.star.util.PathSubstitution" )
  47.             userInstallation =  pathSubst.getSubstituteVariableValue( "user" )
  48.             if len( userInstallation ) > 0:
  49.                 systemPath = uno.fileUrlToSystemPath( userInstallation + "/Scripts/python/log.txt" )
  50.                 ret = file( systemPath , "a" )
  51.         except:
  52.             print "Exception during creation of pythonscript logfile: "+ lastException2String() + "\n, delagating log to stdout\n"
  53.     return ret
  54.   
  55. class Logger(LogLevel):
  56.     def __init__(self , target ):
  57.         self.target = target
  58.  
  59.     def isDebugLevel( self ):
  60.         return self.use >= self.DEBUG
  61.     
  62.     def debug( self, msg ):
  63.         if self.isDebugLevel():
  64.             self.log( self.DEBUG, msg )
  65.     
  66.     def isErrorLevel( self ):
  67.         return self.use >= self.ERROR
  68.  
  69.     def error( self, msg ):
  70.         if self.isErrorLevel():
  71.             self.log( self.ERROR, msg )
  72.  
  73.     def log( self, level, msg ):
  74.         if self.use >= level:
  75.             try:
  76.                 self.target.write(
  77.                     time.asctime() +
  78.                     " [" +
  79.                     logLevel2String( level ) +
  80.                     "] " +
  81.                     encfile(msg) +
  82.                     "\n" )
  83.                 self.target.flush()
  84.             except:
  85.                 print "Error during writing to stdout: " +lastException2String() + "\n"
  86.  
  87. log = Logger( getLogTarget() )
  88.  
  89. log.debug( "pythonscript loading" )
  90.  
  91. #from com.sun.star.lang import typeOfXServiceInfo, typeOfXTypeProvider
  92. from com.sun.star.uno import RuntimeException
  93. from com.sun.star.lang import IllegalArgumentException
  94. from com.sun.star.container import NoSuchElementException
  95. from com.sun.star.lang import XServiceInfo
  96. from com.sun.star.io import IOException
  97. from com.sun.star.ucb import CommandAbortedException, XCommandEnvironment, XProgressHandler
  98. from com.sun.star.task import XInteractionHandler
  99. from com.sun.star.beans import XPropertySet
  100. from com.sun.star.container import XNameContainer
  101. from com.sun.star.xml.sax import XDocumentHandler, InputSource
  102. from com.sun.star.uno import Exception as UnoException
  103. from com.sun.star.script import XInvocation
  104. from com.sun.star.awt import XActionListener
  105.  
  106. from com.sun.star.script.provider import XScriptProvider, XScript, XScriptContext, ScriptFrameworkErrorException
  107. from com.sun.star.script.browse import XBrowseNode
  108. from com.sun.star.script.browse.BrowseNodeTypes import SCRIPT, CONTAINER, ROOT
  109. from com.sun.star.util import XModifyListener
  110.  
  111. LANGUAGENAME = "Python"
  112. GLOBAL_SCRIPTCONTEXT_NAME = "XSCRIPTCONTEXT"
  113. CALLABLE_CONTAINER_NAME =  "g_exportedScripts"
  114.  
  115. # pythonloader looks for a static g_ImplementationHelper variable
  116. g_ImplementationHelper = unohelper.ImplementationHelper()
  117. g_implName = "org.openoffice.pyuno.LanguageScriptProviderFor"+LANGUAGENAME
  118.  
  119.  
  120.  
  121. BLOCK_SIZE = 65536
  122. def readTextFromStream( inputStream ):
  123.     # read the file
  124.     code = uno.ByteSequence( "" )
  125.     while True:
  126.         read,out = inputStream.readBytes( None , BLOCK_SIZE )
  127.         code = code + out
  128.         if read < BLOCK_SIZE:
  129.            break
  130.     return code.value
  131.     
  132. def toIniName( str ):
  133.     # TODO: what is the official way to get to know whether i am on the windows platform ?
  134.     if( hasattr(sys , "dllhandle") ):
  135.         return str + ".ini"
  136.     return str + "rc"
  137.  
  138.  
  139. """ definition: storageURI is the system dependent, absolute file url, where the script is stored on disk
  140.                 scriptURI is the system independent uri
  141. """
  142. class MyUriHelper:
  143.  
  144.     def __init__( self, ctx, location ):
  145.         self.s_UriMap = \
  146.         { "share" : "vnd.sun.star.expand:${$BRAND_BASE_DIR/program/" +  toIniName( "bootstrap") + "::BaseInstallation}/share/Scripts/python" , \
  147.           "share:uno_packages" : "vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE/uno_packages", \
  148.           "user" : "vnd.sun.star.expand:${$BRAND_BASE_DIR/program/" + toIniName( "bootstrap") + "::UserInstallation}/user/Scripts/python" , \
  149.           "user:uno_packages" : "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/uno_packages" } 
  150.         self.m_uriRefFac = ctx.ServiceManager.createInstanceWithContext("com.sun.star.uri.UriReferenceFactory",ctx)
  151.         if location.startswith( "vnd.sun.star.tdoc" ):
  152.             self.m_baseUri = location + "/Scripts/python"
  153.             self.m_scriptUriLocation = "document"
  154.         else:
  155.             self.m_baseUri = expandUri( self.s_UriMap[location] )
  156.             self.m_scriptUriLocation = location
  157.         log.isDebugLevel() and log.debug( "initialized urihelper with baseUri="+self.m_baseUri + ",m_scriptUriLocation="+self.m_scriptUriLocation )
  158.         
  159.     def getRootStorageURI( self ):
  160.         return self.m_baseUri
  161.     
  162.     def getStorageURI( self, scriptURI ):
  163.         return self.scriptURI2StorageUri(scriptURI)
  164.  
  165.     def getScriptURI( self, storageURI ):
  166.         return self.storageURI2ScriptUri(storageURI)
  167.  
  168.     def storageURI2ScriptUri( self, storageURI ):
  169.         if not storageURI.startswith( self.m_baseUri ):
  170.             message = "pythonscript: storage uri '" + storageURI + "' not in base uri '" + self.m_baseUri + "'"
  171.             log.isDebugLevel() and log.debug( message )
  172.             raise RuntimeException( message )
  173.  
  174.         ret = "vnd.sun.star.script:" + \
  175.               storageURI[len(self.m_baseUri)+1:].replace("/","|") + \
  176.               "?language=" + LANGUAGENAME + "&location=" + self.m_scriptUriLocation
  177.         log.isDebugLevel() and log.debug( "converting storageURI="+storageURI + " to scriptURI=" + ret )
  178.         return ret
  179.     
  180.     def scriptURI2StorageUri( self, scriptURI ):
  181.         try:
  182.             myUri = self.m_uriRefFac.parse(scriptURI)
  183.             ret = self.m_baseUri + "/" + myUri.getName().replace( "|", "/" )
  184.             log.isDebugLevel() and log.debug( "converting scriptURI="+scriptURI + " to storageURI=" + ret )
  185.             return ret
  186.         except UnoException, e:
  187.             log.error( "error during converting scriptURI="+scriptURI + ": " + e.Message)
  188.             raise RuntimeException( "pythonscript:scriptURI2StorageUri: " +e.getMessage(), None )
  189.         except Exception, e:
  190.             log.error( "error during converting scriptURI="+scriptURI + ": " + str(e))
  191.             raise RuntimeException( "pythonscript:scriptURI2StorageUri: " + str(e), None )
  192.         
  193.  
  194. class ModuleEntry:
  195.     def __init__( self, lastRead, module ):
  196.         self.lastRead = lastRead
  197.         self.module = module
  198.  
  199. def hasChanged( oldDate, newDate ):
  200.     return newDate.Year > oldDate.Year or \
  201.            newDate.Month > oldDate.Month or \
  202.            newDate.Day > oldDate.Day or \
  203.            newDate.Hours > oldDate.Hours or \
  204.            newDate.Minutes > oldDate.Minutes or \
  205.            newDate.Seconds > oldDate.Seconds or \
  206.            newDate.HundredthSeconds > oldDate.HundredthSeconds
  207.  
  208. def ensureSourceState( code ):
  209.     if not code.endswith( "\n" ):
  210.         code = code + "\n"
  211.     code = code.replace( "\r", "" )
  212.     return code
  213.  
  214.  
  215. def checkForPythonPathBesideScript( url ):
  216.     if url.startswith( "file:" ):
  217.         path = unohelper.fileUrlToSystemPath( url+"/pythonpath.zip" );
  218.         log.log( LogLevel.DEBUG,  "checking for existence of " + path )
  219.         if 1 == os.access( encfile(path), os.F_OK) and not path in sys.path:
  220.             log.log( LogLevel.DEBUG, "adding " + path + " to sys.path" )
  221.             sys.path.append( path )
  222.  
  223.         path = unohelper.fileUrlToSystemPath( url+"/pythonpath" );
  224.         log.log( LogLevel.DEBUG,  "checking for existence of " + path )
  225.         if 1 == os.access( encfile(path), os.F_OK) and not path in sys.path:
  226.             log.log( LogLevel.DEBUG, "adding " + path + " to sys.path" )
  227.             sys.path.append( path )
  228.         
  229.     
  230. class ScriptContext(unohelper.Base):
  231.     def __init__( self, ctx, doc ):
  232.         self.ctx = ctx
  233.         self.doc = doc
  234.        
  235.    # XScriptContext
  236.     def getDocument(self):
  237.         return self.getDesktop().getCurrentComponent()
  238.  
  239.     def getDesktop(self):
  240.         return self.ctx.ServiceManager.createInstanceWithContext(
  241.             "com.sun.star.frame.Desktop", self.ctx )
  242.  
  243.     def getComponentContext(self):
  244.         return self.ctx
  245.  
  246. #----------------------------------
  247. # Global Module Administration
  248. # does not fit together with script
  249. # engine lifetime management
  250. #----------------------------------
  251. #g_scriptContext = ScriptContext( uno.getComponentContext(), None )
  252. #g_modules = {}
  253. #def getModuleByUrl( url, sfa ):
  254. #    entry =  g_modules.get(url)
  255. #    load = True
  256. #    lastRead = sfa.getDateTimeModified( url )
  257. #    if entry:
  258. #        if hasChanged( entry.lastRead, lastRead ):
  259. #            log.isDebugLevel() and log.debug("file " + url + " has changed, reloading")
  260. #        else:
  261. #            load = False
  262. #            
  263. #    if load:
  264. #        log.isDebugLevel() and log.debug( "opening >" + url + "<" )
  265. #
  266. #        code = readTextFromStream( sfa.openFileRead( url ) )
  267.             
  268.         # execute the module
  269. #        entry = ModuleEntry( lastRead, imp.new_module("ooo_script_framework") )
  270. #        entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = g_scriptContext
  271. #        entry.module.__file__ = url
  272. #        exec code in entry.module.__dict__
  273. #        g_modules[ url ] = entry
  274. #        log.isDebugLevel() and log.debug( "mapped " + url + " to " + str( entry.module ) )
  275. #    return entry.module
  276.  
  277. class ProviderContext:
  278.     def __init__( self, storageType, sfa, uriHelper, scriptContext ):
  279.         self.storageType = storageType
  280.         self.sfa = sfa
  281.         self.uriHelper = uriHelper
  282.         self.scriptContext = scriptContext
  283.         self.modules = {}
  284.         self.rootUrl = None
  285.         self.mapPackageName2Path = None
  286.  
  287.     def getTransientPartFromUrl( self, url ):
  288.         rest = url.replace( self.rootUrl , "",1 ).replace( "/","",1)
  289.         return rest[0:rest.find("/")]
  290.     
  291.     def getPackageNameFromUrl( self, url ):
  292.         rest = url.replace( self.rootUrl , "",1 ).replace( "/","",1)
  293.         start = rest.find("/") +1
  294.         return rest[start:rest.find("/",start)]
  295.         
  296.         
  297.     def removePackageByUrl( self, url ):
  298.         items = self.mapPackageName2Path.items()
  299.         for i in items:
  300.             if url in i[1].pathes:
  301.                 self.mapPackageName2Path.pop(i[0])
  302.                 break
  303.  
  304.     def addPackageByUrl( self, url ):
  305.         packageName = self.getPackageNameFromUrl( url )
  306.         transientPart = self.getTransientPartFromUrl( url )
  307.         log.isDebugLevel() and log.debug( "addPackageByUrl : " + packageName + ", " + transientPart + "("+url+")" + ", rootUrl="+self.rootUrl )
  308.         if self.mapPackageName2Path.has_key( packageName ):
  309.             package = self.mapPackageName2Path[ packageName ]
  310.             package.pathes = package.pathes + (url, )
  311.         else:
  312.             package = Package( (url,), transientPart)
  313.             self.mapPackageName2Path[ packageName ] = package
  314.     
  315.     def isUrlInPackage( self, url ):
  316.         values = self.mapPackageName2Path.values()
  317.         for i in values:
  318. #        print "checking " + url + " in " + str(i.pathes)
  319.             if url in i.pathes:
  320.                return True
  321. #        print "false"
  322.         return False
  323.             
  324.     def setPackageAttributes( self, mapPackageName2Path, rootUrl ):
  325.         self.mapPackageName2Path = mapPackageName2Path
  326.         self.rootUrl = rootUrl
  327.         
  328.     def getPersistentUrlFromStorageUrl( self, url ):
  329.         # package name is the second directory
  330.         ret = url
  331.         if self.rootUrl:
  332.             pos = len( self.rootUrl) +1
  333.             ret = url[0:pos]+url[url.find("/",pos)+1:len(url)]
  334.         log.isDebugLevel() and log.debug( "getPersistentUrlFromStorageUrl " + url +  " -> "+ ret)
  335.         return ret
  336.  
  337.     def getStorageUrlFromPersistentUrl( self, url):
  338.         ret = url
  339.         if self.rootUrl:
  340.             pos = len(self.rootUrl)+1
  341.             packageName = url[pos:url.find("/",pos+1)]
  342.             package = self.mapPackageName2Path[ packageName ]
  343.             ret = url[0:pos]+ package.transientPathElement + "/" + url[pos:len(url)]
  344.         log.isDebugLevel() and log.debug( "getStorageUrlFromPersistentUrl " + url + " -> "+ ret)
  345.         return ret
  346.  
  347.     def getFuncsByUrl( self, url ):
  348.         src = readTextFromStream( self.sfa.openFileRead( url ) )
  349.         checkForPythonPathBesideScript( url[0:url.rfind('/')] )
  350.         src = ensureSourceState( src )
  351.  
  352.         code = compiler.parse( src )
  353.  
  354.         allFuncs = []
  355.  
  356.         if code == None:
  357.             return allFuncs
  358.         
  359.         g_exportedScripts = []
  360.         for node in code.node.nodes:
  361.             if node.__class__.__name__ == 'Function':
  362.                 allFuncs.append(node.name)
  363.             elif node.__class__.__name__ == 'Assign':
  364.                 for assignee in node.nodes:
  365.                     if assignee.name == 'g_exportedScripts':
  366.                         for item in node.expr.nodes:
  367.                             if item.__class__.__name__ == 'Name':
  368.                                 g_exportedScripts.append(item.name)
  369.                         return g_exportedScripts
  370.  
  371.         return allFuncs
  372.     
  373.     def getModuleByUrl( self, url ):
  374.         entry =  self.modules.get(url)
  375.         load = True
  376.         lastRead = self.sfa.getDateTimeModified( url )
  377.         if entry:
  378.             if hasChanged( entry.lastRead, lastRead ):
  379.                 log.isDebugLevel() and log.debug( "file " + url + " has changed, reloading" )
  380.             else:
  381.                 load = False
  382.                 
  383.         if load:
  384.             log.isDebugLevel() and log.debug( "opening >" + url + "<" )
  385.             
  386.             src = readTextFromStream( self.sfa.openFileRead( url ) )
  387.             checkForPythonPathBesideScript( url[0:url.rfind('/')] )
  388.             src = ensureSourceState( src )            
  389.             
  390.             # execute the module
  391.             entry = ModuleEntry( lastRead, imp.new_module("ooo_script_framework") )
  392.             entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.scriptContext
  393.  
  394.             code = None
  395.             if url.startswith( "file:" ):
  396.                 code = compile( src, encfile(uno.fileUrlToSystemPath( url ) ), "exec" )
  397.             else:
  398.                 code = compile( src, url, "exec" )
  399.             exec code in entry.module.__dict__
  400.             entry.module.__file__ = url
  401.             self.modules[ url ] = entry
  402.             log.isDebugLevel() and log.debug( "mapped " + url + " to " + str( entry.module ) )
  403.         return  entry.module
  404.         
  405. #--------------------------------------------------
  406. def isScript( candidate ):
  407.     ret = False
  408.     if isinstance( candidate, type(isScript) ):
  409.         ret = True
  410.     return ret
  411.     
  412. #-------------------------------------------------------
  413. class ScriptBrowseNode( unohelper.Base, XBrowseNode , XPropertySet, XInvocation, XActionListener ):
  414.     def __init__( self, provCtx, uri, fileName, funcName ):
  415.         self.fileName = fileName
  416.         self.funcName = funcName
  417.         self.provCtx = provCtx
  418.         self.uri = uri
  419.         
  420.     def getName( self ):
  421.         return self.funcName
  422.  
  423.     def getChildNodes(self):
  424.         return ()
  425.  
  426.     def hasChildNodes(self):
  427.         return False
  428.     
  429.     def getType( self):
  430.         return SCRIPT
  431.  
  432.     def getPropertyValue( self, name ):
  433.         ret = None
  434.         try:
  435.             if name == "URI":
  436.                 ret = self.provCtx.uriHelper.getScriptURI(
  437.                     self.provCtx.getPersistentUrlFromStorageUrl( self.uri + "$" + self.funcName ) )
  438.             elif name == "Editable" and ENABLE_EDIT_DIALOG:
  439.                 ret = not self.provCtx.sfa.isReadOnly( self.uri )
  440.         
  441.             log.isDebugLevel() and log.debug( "ScriptBrowseNode.getPropertyValue called for " + name + ", returning " + str(ret) )
  442.         except:
  443.             log.error( "ScriptBrowseNode.getPropertyValue error " + lastException2String())
  444.             raise
  445.                                               
  446.         return ret
  447.     def setPropertyValue( self, name, value ):
  448.         log.isDebugLevel() and log.debug( "ScriptBrowseNode.setPropertyValue called " + name + "=" +str(value ) )
  449.     def getPropertySetInfo( self ):
  450.         log.isDebugLevel() and log.debug( "ScriptBrowseNode.getPropertySetInfo called "  )
  451.         return None
  452.                
  453.     def getIntrospection( self ):
  454.         return None
  455.  
  456.     def invoke( self, name, params, outparamindex, outparams ):
  457.         if name == "Editable":
  458.             servicename = "com.sun.star.awt.DialogProvider"
  459.             ctx = self.provCtx.scriptContext.getComponentContext()
  460.             dlgprov = ctx.ServiceManager.createInstanceWithContext(
  461.                 servicename, ctx )
  462.  
  463.             self.editor = dlgprov.createDialog(
  464.                 "vnd.sun.star.script:" +
  465.                 "ScriptBindingLibrary.MacroEditor?location=application")
  466.  
  467.             code = readTextFromStream(self.provCtx.sfa.openFileRead(self.uri))
  468.             code = ensureSourceState( code )
  469.             self.editor.getControl("EditorTextField").setText(code)
  470.  
  471.             self.editor.getControl("RunButton").setActionCommand("Run")
  472.             self.editor.getControl("RunButton").addActionListener(self)
  473.             self.editor.getControl("SaveButton").setActionCommand("Save")
  474.             self.editor.getControl("SaveButton").addActionListener(self)
  475.  
  476.             self.editor.execute()
  477.  
  478.         return None
  479.  
  480.     def actionPerformed( self, event ):
  481.         try:
  482.             if event.ActionCommand == "Run":
  483.                 code = self.editor.getControl("EditorTextField").getText()
  484.                 code = ensureSourceState( code )
  485.                 mod = imp.new_module("ooo_script_framework")
  486.                 mod.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.provCtx.scriptContext
  487.                 exec code in mod.__dict__
  488.                 values = mod.__dict__.get( CALLABLE_CONTAINER_NAME , None )
  489.                 if not values:
  490.                     values = mod.__dict__.values()
  491.                     
  492.                 for i in values:
  493.                     if isScript( i ):
  494.                         i()
  495.                         break
  496.                     
  497.             elif event.ActionCommand == "Save":
  498.                 toWrite = uno.ByteSequence(
  499.                     str(
  500.                     self.editor.getControl("EditorTextField").getText().encode(
  501.                     sys.getdefaultencoding())) )
  502.                 copyUrl = self.uri + ".orig"
  503.                 self.provCtx.sfa.move( self.uri, copyUrl )
  504.                 out = self.provCtx.sfa.openFileWrite( self.uri )
  505.                 out.writeBytes( toWrite )
  506.                 out.close()
  507.                 self.provCtx.sfa.kill( copyUrl )
  508. #                log.isDebugLevel() and log.debug("Save is not implemented yet")
  509. #                text = self.editor.getControl("EditorTextField").getText()
  510. #                log.isDebugLevel() and log.debug("Would save: " + text)
  511.         except:
  512.             # TODO: add an error box here !
  513.             log.error( lastException2String() )
  514.             
  515.  
  516.     def setValue( self, name, value ):
  517.         return None
  518.  
  519.     def getValue( self, name ):
  520.         return None
  521.  
  522.     def hasMethod( self, name ):
  523.         return False
  524.  
  525.     def hasProperty( self, name ):
  526.         return False
  527.  
  528.     
  529. #-------------------------------------------------------
  530. class FileBrowseNode( unohelper.Base, XBrowseNode ):
  531.     def __init__( self, provCtx, uri , name ):
  532.         self.provCtx = provCtx
  533.         self.uri = uri
  534.         self.name = name
  535.         self.funcnames = None
  536.         
  537.     def getName( self ):
  538.         return self.name
  539.  
  540.     def getChildNodes(self):
  541.         ret = ()
  542.         try:
  543.             self.funcnames = self.provCtx.getFuncsByUrl( self.uri )
  544.             
  545.             scriptNodeList = []
  546.             for i in self.funcnames:
  547.                 scriptNodeList.append(
  548.                     ScriptBrowseNode(
  549.                     self.provCtx, self.uri, self.name, i ))
  550.             ret = tuple( scriptNodeList )
  551.             log.isDebugLevel() and log.debug( "returning " +str(len(ret)) + " ScriptChildNodes on " + self.uri )
  552.         except:
  553.             text = lastException2String()
  554.             log.error( "Error while evaluating " + self.uri + ":" + text )
  555.             raise
  556.         return ret
  557.  
  558.     def hasChildNodes(self):
  559.         try:
  560.             return len(self.getChildNodes()) > 0
  561.         except:
  562.             return False
  563.     
  564.     def getType( self):
  565.         return CONTAINER
  566.  
  567.         
  568.  
  569. class DirBrowseNode( unohelper.Base, XBrowseNode ):
  570.     def __init__( self, provCtx, name, rootUrl ):
  571.         self.provCtx = provCtx
  572.         self.name = name
  573.         self.rootUrl = rootUrl
  574.  
  575.     def getName( self ):
  576.         return self.name
  577.  
  578.     def getChildNodes( self ):
  579.         try:
  580.             log.isDebugLevel() and log.debug( "DirBrowseNode.getChildNodes called for " + self.rootUrl )
  581.             contents = self.provCtx.sfa.getFolderContents( self.rootUrl, True )
  582.             browseNodeList = []
  583.             for i in contents:
  584.                 if i.endswith( ".py" ):
  585.                     log.isDebugLevel() and log.debug( "adding filenode " + i )
  586.                     browseNodeList.append(
  587.                         FileBrowseNode( self.provCtx, i, i[i.rfind("/")+1:len(i)-3] ) )
  588.                 elif self.provCtx.sfa.isFolder( i ) and not i.endswith("/pythonpath"):
  589.                     log.isDebugLevel() and log.debug( "adding DirBrowseNode " + i )
  590.                     browseNodeList.append( DirBrowseNode( self.provCtx, i[i.rfind("/")+1:len(i)],i))
  591.             return tuple( browseNodeList )
  592.         except Exception, e:
  593.             text = lastException2String()
  594.             log.error( "DirBrowseNode error: " + str(e) + " while evaluating " + self.rootUrl)
  595.             log.error( text)
  596.             return ()
  597.  
  598.     def hasChildNodes( self ):
  599.         return True
  600.  
  601.     def getType( self ):
  602.         return CONTAINER
  603.  
  604.     def getScript( self, uri ):
  605.         log.debug( "DirBrowseNode getScript " + uri + " invoked" )
  606.         raise IllegalArgumentException( "DirBrowseNode couldn't instantiate script " + uri , self , 0 )
  607.  
  608.  
  609. class ManifestHandler( XDocumentHandler, unohelper.Base ):
  610.     def __init__( self, rootUrl ):
  611.         self.rootUrl = rootUrl
  612.         
  613.     def startDocument( self ):
  614.         self.urlList = []
  615.         
  616.     def endDocument( self ):
  617.         pass
  618.         
  619.     def startElement( self , name, attlist):
  620.         if name == "manifest:file-entry":
  621.             if attlist.getValueByName( "manifest:media-type" ) == "application/vnd.sun.star.framework-script":
  622.                 self.urlList.append(
  623.                     self.rootUrl + "/" + attlist.getValueByName( "manifest:full-path" ) )
  624.  
  625.     def endElement( self, name ):
  626.         pass
  627.  
  628.     def characters ( self, chars ):
  629.         pass
  630.  
  631.     def ignoreableWhitespace( self, chars ):
  632.         pass
  633.  
  634.     def setDocumentLocator( self, locator ):
  635.         pass
  636.  
  637. def isPyFileInPath( sfa, path ):
  638.     ret = False
  639.     contents = sfa.getFolderContents( path, True )
  640.     for i in contents:
  641.         if sfa.isFolder(i):
  642.             ret = isPyFileInPath(sfa,i)
  643.         else:
  644.             if i.endswith(".py"):
  645.                 ret = True
  646.         if ret:
  647.             break
  648.     return ret
  649.  
  650. # extracts META-INF directory from 
  651. def getPathesFromPackage( rootUrl, sfa ):
  652.     ret = ()
  653.     try:
  654.         fileUrl = rootUrl + "/META-INF/manifest.xml" 
  655.         inputStream = sfa.openFileRead( fileUrl )
  656.         parser = uno.getComponentContext().ServiceManager.createInstance( "com.sun.star.xml.sax.Parser" )
  657.         handler = ManifestHandler( rootUrl )
  658.         parser.setDocumentHandler( handler )
  659.         parser.parseStream( InputSource( inputStream , "", fileUrl, fileUrl ) )
  660.         for i in tuple(handler.urlList):
  661.             if not isPyFileInPath( sfa, i ):
  662.                 handler.urlList.remove(i)
  663.         ret = tuple( handler.urlList )
  664.     except UnoException:
  665.         text = lastException2String()
  666.         log.debug( "getPathesFromPackage " + fileUrl + " Exception: " +text )
  667.         pass
  668.     return ret
  669.     
  670.  
  671. class Package:
  672.     def __init__( self, pathes, transientPathElement ):
  673.         self.pathes = pathes
  674.         self.transientPathElement = transientPathElement
  675.  
  676. class DummyInteractionHandler( unohelper.Base, XInteractionHandler ):
  677.     def __init__( self ):
  678.         pass
  679.     def handle( self, event):
  680.         log.isDebugLevel() and log.debug( "pythonscript: DummyInteractionHandler.handle " + str( event ) )
  681.  
  682. class DummyProgressHandler( unohelper.Base, XProgressHandler ):
  683.     def __init__( self ):
  684.         pass
  685.     
  686.     def push( self,status ): 
  687.         log.isDebugLevel() and log.debug( "pythonscript: DummyProgressHandler.push " + str( status ) )
  688.     def update( self,status ): 
  689.         log.isDebugLevel() and log.debug( "pythonscript: DummyProgressHandler.update " + str( status ) )
  690.     def pop( self, event ):
  691.         log.isDebugLevel() and log.debug( "pythonscript: DummyProgressHandler.push " + str( event ) )
  692.  
  693. class CommandEnvironment(unohelper.Base, XCommandEnvironment):
  694.     def __init__( self ):
  695.         self.progressHandler = DummyProgressHandler()
  696.         self.interactionHandler = DummyInteractionHandler()
  697.     def getInteractionHandler( self ):
  698.         return self.interactionHandler
  699.     def getProgressHandler( self ):
  700.         return self.progressHandler
  701.  
  702. #maybe useful for debugging purposes
  703. #class ModifyListener( unohelper.Base, XModifyListener ):
  704. #    def __init__( self ):
  705. #        pass
  706. #    def modified( self, event ):
  707. #        log.isDebugLevel() and log.debug( "pythonscript: ModifyListener.modified " + str( event ) )
  708. #    def disposing( self, event ):
  709. #        log.isDebugLevel() and log.debug( "pythonscript: ModifyListener.disposing " + str( event ) )
  710.     
  711. def mapStorageType2PackageContext( storageType ):
  712.     ret = storageType
  713.     if( storageType == "share:uno_packages" ):
  714.         ret = "shared"
  715.     if( storageType == "user:uno_packages" ):
  716.         ret = "user"
  717.     return ret
  718.  
  719. def getPackageName2PathMap( sfa, storageType ):
  720.     ret = {}
  721.     packageManagerFactory = uno.getComponentContext().getValueByName(
  722.         "/singletons/com.sun.star.deployment.thePackageManagerFactory" )
  723.     packageManager = packageManagerFactory.getPackageManager(
  724.         mapStorageType2PackageContext(storageType))
  725. #    packageManager.addModifyListener( ModifyListener() )
  726.     log.isDebugLevel() and log.debug( "pythonscript: getPackageName2PathMap start getDeployedPackages" )
  727.     packages = packageManager.getDeployedPackages(
  728.         packageManager.createAbortChannel(), CommandEnvironment( ) )
  729.     log.isDebugLevel() and log.debug( "pythonscript: getPackageName2PathMap end getDeployedPackages (" + str(len(packages))+")" )
  730.  
  731.     for i in packages:
  732.         log.isDebugLevel() and log.debug( "inspecting package " + i.Name + "("+i.Identifier.Value+")" )
  733.         transientPathElement = penultimateElement( i.URL )
  734.         j = expandUri( i.URL )
  735.         pathes = getPathesFromPackage( j, sfa )
  736.         if len( pathes ) > 0:
  737.             # map package name to url, we need this later
  738.             log.isErrorLevel() and log.error( "adding Package " + transientPathElement + " " + str( pathes ) )
  739.             ret[ lastElement( j ) ] = Package( pathes, transientPathElement )
  740.     return ret
  741.  
  742. def penultimateElement( aStr ):
  743.     lastSlash = aStr.rindex("/")
  744.     penultimateSlash = aStr.rindex("/",0,lastSlash-1)
  745.     return  aStr[ penultimateSlash+1:lastSlash ]
  746.  
  747. def lastElement( aStr):
  748.     return aStr[ aStr.rfind( "/" )+1:len(aStr)]
  749.  
  750. class PackageBrowseNode( unohelper.Base, XBrowseNode ):
  751.     def __init__( self, provCtx, name, rootUrl ):
  752.         self.provCtx = provCtx
  753.         self.name = name
  754.         self.rootUrl = rootUrl
  755.  
  756.     def getName( self ):
  757.         return self.name
  758.  
  759.     def getChildNodes( self ):
  760.         items = self.provCtx.mapPackageName2Path.items()
  761.         browseNodeList = []
  762.         for i in items:
  763.             if len( i[1].pathes ) == 1:
  764.                 browseNodeList.append(
  765.                     DirBrowseNode( self.provCtx, i[0], i[1].pathes[0] ))
  766.             else:
  767.                 for j in i[1].pathes:
  768.                     browseNodeList.append(
  769.                         DirBrowseNode( self.provCtx, i[0]+"."+lastElement(j), j ) )
  770.         return tuple( browseNodeList )
  771.  
  772.     def hasChildNodes( self ):
  773.         return len( self.provCtx.mapPackageName2Path ) > 0
  774.  
  775.     def getType( self ):
  776.         return CONTAINER
  777.  
  778.     def getScript( self, uri ):
  779.         log.debug( "DirBrowseNode getScript " + uri + " invoked" )
  780.         raise IllegalArgumentException( "PackageBrowseNode couldn't instantiate script " + uri , self , 0 )
  781.  
  782.  
  783.  
  784.  
  785. class PythonScript( unohelper.Base, XScript ):
  786.     def __init__( self, func, mod ):
  787.         self.func = func
  788.         self.mod = mod
  789.     def invoke(self, args, out, outindex ):
  790.         log.isDebugLevel() and log.debug( "PythonScript.invoke " + str( args ) )
  791.         try:
  792.             ret = self.func( *args )
  793.         except UnoException,e:
  794.             # UNO Exception continue to fly ...
  795.             text = lastException2String()
  796.             complete = "Error during invoking function " + \
  797.                 str(self.func.__name__) + " in module " + \
  798.                 self.mod.__file__ + " (" + text + ")"
  799.             log.isDebugLevel() and log.debug( complete )
  800.             # some people may beat me up for modifying the exception text,
  801.             # but otherwise office just shows
  802.             # the type name and message text with no more information,
  803.             # this is really bad for most users. 
  804.             e.Message = e.Message + " (" + complete + ")"
  805.             raise
  806.         except Exception,e:
  807.             # General python exception are converted to uno RuntimeException
  808.             text = lastException2String()
  809.             complete = "Error during invoking function " + \
  810.                 str(self.func.__name__) + " in module " + \
  811.                 self.mod.__file__ + " (" + text + ")"
  812.             log.isDebugLevel() and log.debug( complete )
  813.             raise RuntimeException( complete , self )
  814.         log.isDebugLevel() and log.debug( "PythonScript.invoke ret = " + str( ret ) )
  815.         return ret, (), ()
  816.  
  817. def expandUri(  uri ):
  818.     if uri.startswith( "vnd.sun.star.expand:" ):
  819.         uri = uri.replace( "vnd.sun.star.expand:", "",1)
  820.         uri = uno.getComponentContext().getByName(
  821.                     "/singletons/com.sun.star.util.theMacroExpander" ).expandMacros( uri )
  822.     if uri.startswith( "file:" ):
  823.         uri = uno.absolutize("",uri)   # necessary to get rid of .. in uri
  824.     return uri
  825.     
  826. #--------------------------------------------------------------
  827. class PythonScriptProvider( unohelper.Base, XBrowseNode, XScriptProvider, XNameContainer):
  828.     def __init__( self, ctx, *args ):
  829.         if log.isDebugLevel():
  830.             mystr = ""
  831.             for i in args:
  832.                 if len(mystr) > 0:
  833.                     mystr = mystr +","
  834.                 mystr = mystr + str(i)
  835.             log.debug( "Entering PythonScriptProvider.ctor" + mystr )
  836.  
  837.         storageType = ""
  838.         if isinstance(args[0],unicode ):
  839.             storageType = args[0]
  840.         else:
  841.             storageType = args[0].SCRIPTING_DOC_URI
  842.         isPackage = storageType.endswith( ":uno_packages" )
  843.  
  844.         try:
  845. #            urlHelper = ctx.ServiceManager.createInstanceWithArgumentsAndContext(
  846. #                "com.sun.star.script.provider.ScriptURIHelper", (LANGUAGENAME, storageType), ctx)
  847.             urlHelper = MyUriHelper( ctx, storageType )
  848.             log.isDebugLevel() and log.debug( "got urlHelper " + str( urlHelper ) )
  849.         
  850.             rootUrl = expandUri( urlHelper.getRootStorageURI() )
  851.             log.isDebugLevel() and log.debug( storageType + " transformed to " + rootUrl )
  852.  
  853.             ucbService = "com.sun.star.ucb.SimpleFileAccess"
  854.             sfa = ctx.ServiceManager.createInstanceWithContext( ucbService, ctx )
  855.             if not sfa:
  856.                 log.debug("PythonScriptProvider couldn't instantiate " +ucbService)
  857.                 raise RuntimeException(
  858.                     "PythonScriptProvider couldn't instantiate " +ucbService, self)
  859.             self.provCtx = ProviderContext(
  860.                 storageType, sfa, urlHelper, ScriptContext( uno.getComponentContext(), None ) )
  861.             if isPackage:
  862.                 mapPackageName2Path = getPackageName2PathMap( sfa, storageType )
  863.                 self.provCtx.setPackageAttributes( mapPackageName2Path , rootUrl )
  864.                 self.dirBrowseNode = PackageBrowseNode( self.provCtx, LANGUAGENAME, rootUrl )
  865.             else:
  866.                 self.dirBrowseNode = DirBrowseNode( self.provCtx, LANGUAGENAME, rootUrl )
  867.             
  868.         except Exception, e:
  869.             text = lastException2String()
  870.             log.debug( "PythonScriptProvider could not be instantiated because of : " + text )
  871.             raise e
  872.  
  873.     def getName( self ):
  874.         return self.dirBrowseNode.getName()
  875.  
  876.     def getChildNodes( self ):
  877.         return self.dirBrowseNode.getChildNodes()    
  878.  
  879.     def hasChildNodes( self ):
  880.         return self.dirBrowseNode.hasChildNodes()
  881.  
  882.     def getType( self ):
  883.         return self.dirBrowseNode.getType()
  884.  
  885.     def getScript( self, uri ):
  886.         log.debug( "DirBrowseNode getScript " + uri + " invoked" )
  887.         
  888.         raise IllegalArgumentException( "DirBrowseNode couldn't instantiate script " + uri , self , 0 )
  889.  
  890.     def getScript( self, scriptUri ):
  891.         try:
  892.             log.isDebugLevel() and log.debug( "getScript " + scriptUri + " invoked")
  893.             
  894.             storageUri = self.provCtx.getStorageUrlFromPersistentUrl(
  895.                 self.provCtx.uriHelper.getStorageURI(scriptUri) );
  896.             log.isDebugLevel() and log.debug( "getScript: storageUri = " + storageUri)
  897.             fileUri = storageUri[0:storageUri.find( "$" )]
  898.             funcName = storageUri[storageUri.find( "$" )+1:len(storageUri)]        
  899.             
  900.             mod = self.provCtx.getModuleByUrl( fileUri )
  901.             log.isDebugLevel() and log.debug( " got mod " + str(mod) )
  902.             
  903.             func = mod.__dict__[ funcName ]
  904.  
  905.             log.isDebugLevel() and log.debug( "got func " + str( func ) )
  906.             return PythonScript( func, mod )
  907.         except:
  908.             text = lastException2String()
  909.             log.error( text )
  910.             raise ScriptFrameworkErrorException( text, self, scriptUri, LANGUAGENAME, 0 )
  911.         
  912.  
  913.     # XServiceInfo
  914.     def getSupportedServices( self ):
  915.         return g_ImplementationHelper.getSupportedServices(g_implName)
  916.  
  917.     def supportsService( self, ServiceName ):
  918.         return g_ImplementationHelper.supportsService( g_implName, ServiceName )
  919.  
  920.     def getImplementationName(self):
  921.         return g_implName
  922.  
  923.     def getByName( self, name ):
  924.         log.debug( "getByName called" + str( name ))
  925.         return None
  926.  
  927.         
  928.     def getElementNames( self ):
  929.         log.debug( "getElementNames called")
  930.         return ()
  931.     
  932.     def hasByName( self, name ):
  933.         try:
  934.             log.debug( "hasByName called " + str( name ))
  935.             uri = expandUri(name)
  936.             ret = self.provCtx.isUrlInPackage( uri )
  937.             log.debug( "hasByName " + uri + " " +str( ret ) )
  938.             return ret
  939.         except:
  940.             text = lastException2String()
  941.             log.debug( "Error in hasByName:" +  text )
  942.             return False
  943.  
  944.     def removeByName( self, name ):
  945.         log.debug( "removeByName called" + str( name ))
  946.         uri = expandUri( name )
  947.         if self.provCtx.isUrlInPackage( uri ):
  948.             self.provCtx.removePackageByUrl( uri )
  949.         else:
  950.             log.debug( "removeByName unknown uri " + str( name ) + ", ignoring" )
  951.             raise NoSuchElementException( uri + "is not in package" , self )
  952.         log.debug( "removeByName called" + str( uri ) + " successful" )
  953.         
  954.     def insertByName( self, name, value ):
  955.         log.debug( "insertByName called " + str( name ) + " " + str( value ))
  956.         uri = expandUri( name )
  957.         if isPyFileInPath( self.provCtx.sfa, uri ):
  958.             self.provCtx.addPackageByUrl( uri )
  959.         else:
  960.             # package is no python package ...
  961.             log.debug( "insertByName: no python files in " + str( uri ) + ", ignoring" )
  962.             raise IllegalArgumentException( uri + " does not contain .py files", self, 1 )
  963.         log.debug( "insertByName called " + str( uri ) + " successful" )
  964.  
  965.     def replaceByName( self, name, value ):
  966.         log.debug( "replaceByName called " + str( name ) + " " + str( value ))
  967.         uri = expandUri( name )
  968.         self.removeByName( name )
  969.         self.insertByName( name, value )
  970.         log.debug( "replaceByName called" + str( uri ) + " successful" )
  971.  
  972.     def getElementType( self ):
  973.         log.debug( "getElementType called" )
  974.         return uno.getTypeByName( "void" )
  975.     
  976.     def hasElements( self ):
  977.         log.debug( "hasElements got called")
  978.         return False
  979.     
  980. g_ImplementationHelper.addImplementation( \
  981.     PythonScriptProvider,g_implName, \
  982.     ("com.sun.star.script.provider.LanguageScriptProvider",
  983.      "com.sun.star.script.provider.ScriptProviderFor"+ LANGUAGENAME,),)
  984.  
  985.  
  986. log.debug( "pythonscript finished intializing" )
  987.  
  988.