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