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, Command 117from com.sun.star.task import XInteractionHandler 118from com.sun.star.beans import XPropertySet, Property 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, inv ): 251 self.ctx = ctx 252 self.doc = doc 253 self.inv = inv 254 255 # XScriptContext 256 def getDocument(self): 257 if self.doc: 258 return self.doc 259 return self.getDesktop().getCurrentComponent() 260 261 def getDesktop(self): 262 return self.ctx.ServiceManager.createInstanceWithContext( 263 "com.sun.star.frame.Desktop", self.ctx ) 264 265 def getComponentContext(self): 266 return self.ctx 267 268 def getInvocationContext(self): 269 return self.inv 270 271#---------------------------------- 272# Global Module Administration 273# does not fit together with script 274# engine lifetime management 275#---------------------------------- 276#g_scriptContext = ScriptContext( uno.getComponentContext(), None ) 277#g_modules = {} 278#def getModuleByUrl( url, sfa ): 279# entry = g_modules.get(url) 280# load = True 281# lastRead = sfa.getDateTimeModified( url ) 282# if entry: 283# if hasChanged( entry.lastRead, lastRead ): 284# log.isDebugLevel() and log.debug("file " + url + " has changed, reloading") 285# else: 286# load = False 287# 288# if load: 289# log.isDebugLevel() and log.debug( "opening >" + url + "<" ) 290# 291# code = readTextFromStream( sfa.openFileRead( url ) ) 292 293 # execute the module 294# entry = ModuleEntry( lastRead, imp.new_module("ooo_script_framework") ) 295# entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = g_scriptContext 296# entry.module.__file__ = url 297# exec code in entry.module.__dict__ 298# g_modules[ url ] = entry 299# log.isDebugLevel() and log.debug( "mapped " + url + " to " + str( entry.module ) ) 300# return entry.module 301 302class ProviderContext: 303 def __init__( self, storageType, sfa, uriHelper, scriptContext ): 304 self.storageType = storageType 305 self.sfa = sfa 306 self.uriHelper = uriHelper 307 self.scriptContext = scriptContext 308 self.modules = {} 309 self.rootUrl = None 310 self.mapPackageName2Path = None 311 312 def getTransientPartFromUrl( self, url ): 313 rest = url.replace( self.rootUrl , "",1 ).replace( "/","",1) 314 return rest[0:rest.find("/")] 315 316 def getPackageNameFromUrl( self, url ): 317 rest = url.replace( self.rootUrl , "",1 ).replace( "/","",1) 318 start = rest.find("/") +1 319 return rest[start:rest.find("/",start)] 320 321 322 def removePackageByUrl( self, url ): 323 items = self.mapPackageName2Path.items() 324 for i in items: 325 if url in i[1].pathes: 326 self.mapPackageName2Path.pop(i[0]) 327 break 328 329 def addPackageByUrl( self, url ): 330 packageName = self.getPackageNameFromUrl( url ) 331 transientPart = self.getTransientPartFromUrl( url ) 332 log.isDebugLevel() and log.debug( "addPackageByUrl : " + packageName + ", " + transientPart + "("+url+")" + ", rootUrl="+self.rootUrl ) 333 if self.mapPackageName2Path.has_key( packageName ): 334 package = self.mapPackageName2Path[ packageName ] 335 package.pathes = package.pathes + (url, ) 336 else: 337 package = Package( (url,), transientPart) 338 self.mapPackageName2Path[ packageName ] = package 339 340 def isUrlInPackage( self, url ): 341 values = self.mapPackageName2Path.values() 342 for i in values: 343# print "checking " + url + " in " + str(i.pathes) 344 if url in i.pathes: 345 return True 346# print "false" 347 return False 348 349 def setPackageAttributes( self, mapPackageName2Path, rootUrl ): 350 self.mapPackageName2Path = mapPackageName2Path 351 self.rootUrl = rootUrl 352 353 def getPersistentUrlFromStorageUrl( self, url ): 354 # package name is the second directory 355 ret = url 356 if self.rootUrl: 357 pos = len( self.rootUrl) +1 358 ret = url[0:pos]+url[url.find("/",pos)+1:len(url)] 359 log.isDebugLevel() and log.debug( "getPersistentUrlFromStorageUrl " + url + " -> "+ ret) 360 return ret 361 362 def getStorageUrlFromPersistentUrl( self, url): 363 ret = url 364 if self.rootUrl: 365 pos = len(self.rootUrl)+1 366 packageName = url[pos:url.find("/",pos+1)] 367 package = self.mapPackageName2Path[ packageName ] 368 ret = url[0:pos]+ package.transientPathElement + "/" + url[pos:len(url)] 369 log.isDebugLevel() and log.debug( "getStorageUrlFromPersistentUrl " + url + " -> "+ ret) 370 return ret 371 372 def getFuncsByUrl( self, url ): 373 src = readTextFromStream( self.sfa.openFileRead( url ) ) 374 checkForPythonPathBesideScript( url[0:url.rfind('/')] ) 375 src = ensureSourceState( src ) 376 377 code = compiler.parse( src ) 378 379 allFuncs = [] 380 381 if code == None: 382 return allFuncs 383 384 g_exportedScripts = [] 385 for node in code.node.nodes: 386 if node.__class__.__name__ == 'Function': 387 allFuncs.append(node.name) 388 elif node.__class__.__name__ == 'Assign': 389 for assignee in node.nodes: 390 if assignee.name == 'g_exportedScripts': 391 for item in node.expr: 392 if item.__class__.__name__ == 'Name': 393 g_exportedScripts.append(item.name) 394 return g_exportedScripts 395 396 return allFuncs 397 398 def getModuleByUrl( self, url ): 399 entry = self.modules.get(url) 400 load = True 401 lastRead = self.sfa.getDateTimeModified( url ) 402 if entry: 403 if hasChanged( entry.lastRead, lastRead ): 404 log.isDebugLevel() and log.debug( "file " + url + " has changed, reloading" ) 405 else: 406 load = False 407 408 if load: 409 log.isDebugLevel() and log.debug( "opening >" + url + "<" ) 410 411 src = readTextFromStream( self.sfa.openFileRead( url ) ) 412 checkForPythonPathBesideScript( url[0:url.rfind('/')] ) 413 src = ensureSourceState( src ) 414 415 # execute the module 416 entry = ModuleEntry( lastRead, imp.new_module("ooo_script_framework") ) 417 entry.module.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.scriptContext 418 419 code = None 420 if url.startswith( "file:" ): 421 code = compile( src, encfile(uno.fileUrlToSystemPath( url ) ), "exec" ) 422 else: 423 code = compile( src, url, "exec" ) 424 exec code in entry.module.__dict__ 425 entry.module.__file__ = url 426 self.modules[ url ] = entry 427 log.isDebugLevel() and log.debug( "mapped " + url + " to " + str( entry.module ) ) 428 return entry.module 429 430#-------------------------------------------------- 431def isScript( candidate ): 432 ret = False 433 if isinstance( candidate, type(isScript) ): 434 ret = True 435 return ret 436 437#------------------------------------------------------- 438class ScriptBrowseNode( unohelper.Base, XBrowseNode , XPropertySet, XInvocation, XActionListener ): 439 def __init__( self, provCtx, uri, fileName, funcName ): 440 self.fileName = fileName 441 self.funcName = funcName 442 self.provCtx = provCtx 443 self.uri = uri 444 445 def getName( self ): 446 return self.funcName 447 448 def getChildNodes(self): 449 return () 450 451 def hasChildNodes(self): 452 return False 453 454 def getType( self): 455 return SCRIPT 456 457 def getPropertyValue( self, name ): 458 ret = None 459 try: 460 if name == "URI": 461 ret = self.provCtx.uriHelper.getScriptURI( 462 self.provCtx.getPersistentUrlFromStorageUrl( self.uri + "$" + self.funcName ) ) 463 elif name == "Editable" and ENABLE_EDIT_DIALOG: 464 ret = not self.provCtx.sfa.isReadOnly( self.uri ) 465 466 log.isDebugLevel() and log.debug( "ScriptBrowseNode.getPropertyValue called for " + name + ", returning " + str(ret) ) 467 except Exception,e: 468 log.error( "ScriptBrowseNode.getPropertyValue error " + lastException2String()) 469 raise 470 471 return ret 472 def setPropertyValue( self, name, value ): 473 log.isDebugLevel() and log.debug( "ScriptBrowseNode.setPropertyValue called " + name + "=" +str(value ) ) 474 def getPropertySetInfo( self ): 475 log.isDebugLevel() and log.debug( "ScriptBrowseNode.getPropertySetInfo called " ) 476 return None 477 478 def getIntrospection( self ): 479 return None 480 481 def invoke( self, name, params, outparamindex, outparams ): 482 if name == "Editable": 483 servicename = "com.sun.star.awt.DialogProvider" 484 ctx = self.provCtx.scriptContext.getComponentContext() 485 dlgprov = ctx.ServiceManager.createInstanceWithContext( 486 servicename, ctx ) 487 488 self.editor = dlgprov.createDialog( 489 "vnd.sun.star.script:" + 490 "ScriptBindingLibrary.MacroEditor?location=application") 491 492 code = readTextFromStream(self.provCtx.sfa.openFileRead(self.uri)) 493 code = ensureSourceState( code ) 494 self.editor.getControl("EditorTextField").setText(code) 495 496 self.editor.getControl("RunButton").setActionCommand("Run") 497 self.editor.getControl("RunButton").addActionListener(self) 498 self.editor.getControl("SaveButton").setActionCommand("Save") 499 self.editor.getControl("SaveButton").addActionListener(self) 500 501 self.editor.execute() 502 503 return None 504 505 def actionPerformed( self, event ): 506 try: 507 if event.ActionCommand == "Run": 508 code = self.editor.getControl("EditorTextField").getText() 509 code = ensureSourceState( code ) 510 mod = imp.new_module("ooo_script_framework") 511 mod.__dict__[GLOBAL_SCRIPTCONTEXT_NAME] = self.provCtx.scriptContext 512 exec code in mod.__dict__ 513 values = mod.__dict__.get( CALLABLE_CONTAINER_NAME , None ) 514 if not values: 515 values = mod.__dict__.values() 516 517 for i in values: 518 if isScript( i ): 519 i() 520 break 521 522 elif event.ActionCommand == "Save": 523 toWrite = uno.ByteSequence( 524 str( 525 self.editor.getControl("EditorTextField").getText().encode( 526 sys.getdefaultencoding())) ) 527 copyUrl = self.uri + ".orig" 528 self.provCtx.sfa.move( self.uri, copyUrl ) 529 out = self.provCtx.sfa.openFileWrite( self.uri ) 530 out.writeBytes( toWrite ) 531 out.close() 532 self.provCtx.sfa.kill( copyUrl ) 533# log.isDebugLevel() and log.debug("Save is not implemented yet") 534# text = self.editor.getControl("EditorTextField").getText() 535# log.isDebugLevel() and log.debug("Would save: " + text) 536 except Exception,e: 537 # TODO: add an error box here ! 538 log.error( lastException2String() ) 539 540 541 def setValue( self, name, value ): 542 return None 543 544 def getValue( self, name ): 545 return None 546 547 def hasMethod( self, name ): 548 return False 549 550 def hasProperty( self, name ): 551 return False 552 553 554#------------------------------------------------------- 555class FileBrowseNode( unohelper.Base, XBrowseNode ): 556 def __init__( self, provCtx, uri , name ): 557 self.provCtx = provCtx 558 self.uri = uri 559 self.name = name 560 self.funcnames = None 561 562 def getName( self ): 563 return self.name 564 565 def getChildNodes(self): 566 ret = () 567 try: 568 self.funcnames = self.provCtx.getFuncsByUrl( self.uri ) 569 570 scriptNodeList = [] 571 for i in self.funcnames: 572 scriptNodeList.append( 573 ScriptBrowseNode( 574 self.provCtx, self.uri, self.name, i )) 575 ret = tuple( scriptNodeList ) 576 log.isDebugLevel() and log.debug( "returning " +str(len(ret)) + " ScriptChildNodes on " + self.uri ) 577 except Exception, e: 578 text = lastException2String() 579 log.error( "Error while evaluating " + self.uri + ":" + text ) 580 raise 581 return ret 582 583 def hasChildNodes(self): 584 try: 585 return len(self.getChildNodes()) > 0 586 except Exception, e: 587 return False 588 589 def getType( self): 590 return CONTAINER 591 592 593 594class DirBrowseNode( unohelper.Base, XBrowseNode ): 595 def __init__( self, provCtx, name, rootUrl ): 596 self.provCtx = provCtx 597 self.name = name 598 self.rootUrl = rootUrl 599 600 def getName( self ): 601 return self.name 602 603 def getChildNodes( self ): 604 try: 605 log.isDebugLevel() and log.debug( "DirBrowseNode.getChildNodes called for " + self.rootUrl ) 606 contents = self.provCtx.sfa.getFolderContents( self.rootUrl, True ) 607 browseNodeList = [] 608 for i in contents: 609 if i.endswith( ".py" ): 610 log.isDebugLevel() and log.debug( "adding filenode " + i ) 611 browseNodeList.append( 612 FileBrowseNode( self.provCtx, i, i[i.rfind("/")+1:len(i)-3] ) ) 613 elif self.provCtx.sfa.isFolder( i ) and not i.endswith("/pythonpath"): 614 log.isDebugLevel() and log.debug( "adding DirBrowseNode " + i ) 615 browseNodeList.append( DirBrowseNode( self.provCtx, i[i.rfind("/")+1:len(i)],i)) 616 return tuple( browseNodeList ) 617 except Exception, e: 618 text = lastException2String() 619 log.error( "DirBrowseNode error: " + str(e) + " while evaluating " + self.rootUrl) 620 log.error( text) 621 return () 622 623 def hasChildNodes( self ): 624 return True 625 626 def getType( self ): 627 return CONTAINER 628 629 def getScript( self, uri ): 630 log.debug( "DirBrowseNode getScript " + uri + " invoked" ) 631 raise IllegalArgumentException( "DirBrowseNode couldn't instantiate script " + uri , self , 0 ) 632 633 634class ManifestHandler( XDocumentHandler, unohelper.Base ): 635 def __init__( self, rootUrl ): 636 self.rootUrl = rootUrl 637 638 def startDocument( self ): 639 self.urlList = [] 640 641 def endDocument( self ): 642 pass 643 644 def startElement( self , name, attlist): 645 if name == "manifest:file-entry": 646 if attlist.getValueByName( "manifest:media-type" ) == "application/vnd.sun.star.framework-script": 647 self.urlList.append( 648 self.rootUrl + "/" + attlist.getValueByName( "manifest:full-path" ) ) 649 650 def endElement( self, name ): 651 pass 652 653 def characters ( self, chars ): 654 pass 655 656 def ignoreableWhitespace( self, chars ): 657 pass 658 659 def setDocumentLocator( self, locator ): 660 pass 661 662def isPyFileInPath( sfa, path ): 663 ret = False 664 contents = sfa.getFolderContents( path, True ) 665 for i in contents: 666 if sfa.isFolder(i): 667 ret = isPyFileInPath(sfa,i) 668 else: 669 if i.endswith(".py"): 670 ret = True 671 if ret: 672 break 673 return ret 674 675# extracts META-INF directory from 676def getPathesFromPackage( rootUrl, sfa ): 677 ret = () 678 try: 679 fileUrl = rootUrl + "/META-INF/manifest.xml" 680 inputStream = sfa.openFileRead( fileUrl ) 681 parser = uno.getComponentContext().ServiceManager.createInstance( "com.sun.star.xml.sax.Parser" ) 682 handler = ManifestHandler( rootUrl ) 683 parser.setDocumentHandler( handler ) 684 parser.parseStream( InputSource( inputStream , "", fileUrl, fileUrl ) ) 685 for i in tuple(handler.urlList): 686 if not isPyFileInPath( sfa, i ): 687 handler.urlList.remove(i) 688 ret = tuple( handler.urlList ) 689 except UnoException, e: 690 text = lastException2String() 691 log.debug( "getPathesFromPackage " + fileUrl + " Exception: " +text ) 692 pass 693 return ret 694 695 696class Package: 697 def __init__( self, pathes, transientPathElement ): 698 self.pathes = pathes 699 self.transientPathElement = transientPathElement 700 701class DummyInteractionHandler( unohelper.Base, XInteractionHandler ): 702 def __init__( self ): 703 pass 704 def handle( self, event): 705 log.isDebugLevel() and log.debug( "pythonscript: DummyInteractionHandler.handle " + str( event ) ) 706 707class DummyProgressHandler( unohelper.Base, XProgressHandler ): 708 def __init__( self ): 709 pass 710 711 def push( self,status ): 712 log.isDebugLevel() and log.debug( "pythonscript: DummyProgressHandler.push " + str( status ) ) 713 def update( self,status ): 714 log.isDebugLevel() and log.debug( "pythonscript: DummyProgressHandler.update " + str( status ) ) 715 def pop( self ): 716 log.isDebugLevel() and log.debug( "pythonscript: DummyProgressHandler.push " + str( event ) ) 717 718class CommandEnvironment(unohelper.Base, XCommandEnvironment): 719 def __init__( self ): 720 self.progressHandler = DummyProgressHandler() 721 self.interactionHandler = DummyInteractionHandler() 722 def getInteractionHandler( self ): 723 return self.interactionHandler 724 def getProgressHandler( self ): 725 return self.progressHandler 726 727#maybe useful for debugging purposes 728#class ModifyListener( unohelper.Base, XModifyListener ): 729# def __init__( self ): 730# pass 731# def modified( self, event ): 732# log.isDebugLevel() and log.debug( "pythonscript: ModifyListener.modified " + str( event ) ) 733# def disposing( self, event ): 734# log.isDebugLevel() and log.debug( "pythonscript: ModifyListener.disposing " + str( event ) ) 735 736def getModelFromDocUrl(ctx, url): 737 """Get document model from document url.""" 738 doc = None 739 args = ("Local", "Office") 740 ucb = ctx.getServiceManager().createInstanceWithArgumentsAndContext( 741 "com.sun.star.ucb.UniversalContentBroker", args, ctx) 742 identifier = ucb.createContentIdentifier(url) 743 content = ucb.queryContent(identifier) 744 p = Property() 745 p.Name = "DocumentModel" 746 p.Handle = -1 747 748 c = Command() 749 c.Handle = -1 750 c.Name = "getPropertyValues" 751 c.Argument = uno.Any("[]com.sun.star.beans.Property", (p,)) 752 753 env = CommandEnvironment() 754 try: 755 ret = content.execute(c, 0, env) 756 doc = ret.getObject(1, None) 757 except Exception, e: 758 log.isErrorLevel() and log.error("getModelFromDocUrl: %s" % url) 759 return doc 760 761def mapStorageType2PackageContext( storageType ): 762 ret = storageType 763 if( storageType == "share:uno_packages" ): 764 ret = "shared" 765 if( storageType == "user:uno_packages" ): 766 ret = "user" 767 return ret 768 769def getPackageName2PathMap( sfa, storageType ): 770 ret = {} 771 packageManagerFactory = uno.getComponentContext().getValueByName( 772 "/singletons/com.sun.star.deployment.thePackageManagerFactory" ) 773 packageManager = packageManagerFactory.getPackageManager( 774 mapStorageType2PackageContext(storageType)) 775# packageManager.addModifyListener( ModifyListener() ) 776 log.isDebugLevel() and log.debug( "pythonscript: getPackageName2PathMap start getDeployedPackages" ) 777 packages = packageManager.getDeployedPackages( 778 packageManager.createAbortChannel(), CommandEnvironment( ) ) 779 log.isDebugLevel() and log.debug( "pythonscript: getPackageName2PathMap end getDeployedPackages (" + str(len(packages))+")" ) 780 781 for i in packages: 782 log.isDebugLevel() and log.debug( "inspecting package " + i.Name + "("+i.Identifier.Value+")" ) 783 transientPathElement = penultimateElement( i.URL ) 784 j = expandUri( i.URL ) 785 pathes = getPathesFromPackage( j, sfa ) 786 if len( pathes ) > 0: 787 # map package name to url, we need this later 788 log.isErrorLevel() and log.error( "adding Package " + transientPathElement + " " + str( pathes ) ) 789 ret[ lastElement( j ) ] = Package( pathes, transientPathElement ) 790 return ret 791 792def penultimateElement( aStr ): 793 lastSlash = aStr.rindex("/") 794 penultimateSlash = aStr.rindex("/",0,lastSlash-1) 795 return aStr[ penultimateSlash+1:lastSlash ] 796 797def lastElement( aStr): 798 return aStr[ aStr.rfind( "/" )+1:len(aStr)] 799 800class PackageBrowseNode( unohelper.Base, XBrowseNode ): 801 def __init__( self, provCtx, name, rootUrl ): 802 self.provCtx = provCtx 803 self.name = name 804 self.rootUrl = rootUrl 805 806 def getName( self ): 807 return self.name 808 809 def getChildNodes( self ): 810 items = self.provCtx.mapPackageName2Path.items() 811 browseNodeList = [] 812 for i in items: 813 if len( i[1].pathes ) == 1: 814 browseNodeList.append( 815 DirBrowseNode( self.provCtx, i[0], i[1].pathes[0] )) 816 else: 817 for j in i[1].pathes: 818 browseNodeList.append( 819 DirBrowseNode( self.provCtx, i[0]+"."+lastElement(j), j ) ) 820 return tuple( browseNodeList ) 821 822 def hasChildNodes( self ): 823 return len( self.mapPackageName2Path ) > 0 824 825 def getType( self ): 826 return CONTAINER 827 828 def getScript( self, uri ): 829 log.debug( "DirBrowseNode getScript " + uri + " invoked" ) 830 raise IllegalArgumentException( "PackageBrowseNode couldn't instantiate script " + uri , self , 0 ) 831 832 833 834 835class PythonScript( unohelper.Base, XScript ): 836 def __init__( self, func, mod ): 837 self.func = func 838 self.mod = mod 839 def invoke(self, args, out, outindex ): 840 log.isDebugLevel() and log.debug( "PythonScript.invoke " + str( args ) ) 841 try: 842 ret = self.func( *args ) 843 except UnoException,e: 844 # UNO Exception continue to fly ... 845 text = lastException2String() 846 complete = "Error during invoking function " + \ 847 str(self.func.__name__) + " in module " + \ 848 self.mod.__file__ + " (" + text + ")" 849 log.isDebugLevel() and log.debug( complete ) 850 # some people may beat me up for modifying the exception text, 851 # but otherwise office just shows 852 # the type name and message text with no more information, 853 # this is really bad for most users. 854 e.Message = e.Message + " (" + complete + ")" 855 raise 856 except Exception,e: 857 # General python exception are converted to uno RuntimeException 858 text = lastException2String() 859 complete = "Error during invoking function " + \ 860 str(self.func.__name__) + " in module " + \ 861 self.mod.__file__ + " (" + text + ")" 862 log.isDebugLevel() and log.debug( complete ) 863 raise RuntimeException( complete , self ) 864 log.isDebugLevel() and log.debug( "PythonScript.invoke ret = " + str( ret ) ) 865 return ret, (), () 866 867def expandUri( uri ): 868 if uri.startswith( "vnd.sun.star.expand:" ): 869 uri = uri.replace( "vnd.sun.star.expand:", "",1) 870 uri = uno.getComponentContext().getByName( 871 "/singletons/com.sun.star.util.theMacroExpander" ).expandMacros( uri ) 872 if uri.startswith( "file:" ): 873 uri = uno.absolutize("",uri) # necessary to get rid of .. in uri 874 return uri 875 876#-------------------------------------------------------------- 877class PythonScriptProvider( unohelper.Base, XBrowseNode, XScriptProvider, XNameContainer): 878 def __init__( self, ctx, *args ): 879 if log.isDebugLevel(): 880 mystr = "" 881 for i in args: 882 if len(mystr) > 0: 883 mystr = mystr +"," 884 mystr = mystr + str(i) 885 log.debug( "Entering PythonScriptProvider.ctor" + mystr ) 886 887 doc = None 888 inv = None 889 storageType = "" 890 891 if isinstance(args[0],unicode ): 892 storageType = args[0] 893 if storageType.startswith( "vnd.sun.star.tdoc" ): 894 doc = getModelFromDocUrl(ctx, storageType) 895 else: 896 inv = args[0] 897 try: 898 doc = inv.ScriptContainer 899 content = ctx.getServiceManager().createInstanceWithContext( 900 "com.sun.star.frame.TransientDocumentsDocumentContentFactory", 901 ctx).createDocumentContent(doc) 902 storageType = content.getIdentifier().getContentIdentifier() 903 except Exception, e: 904 text = lastException2String() 905 log.error( text ) 906 907 isPackage = storageType.endswith( ":uno_packages" ) 908 909 try: 910# urlHelper = ctx.ServiceManager.createInstanceWithArgumentsAndContext( 911# "com.sun.star.script.provider.ScriptURIHelper", (LANGUAGENAME, storageType), ctx) 912 urlHelper = MyUriHelper( ctx, storageType ) 913 log.isDebugLevel() and log.debug( "got urlHelper " + str( urlHelper ) ) 914 915 rootUrl = expandUri( urlHelper.getRootStorageURI() ) 916 log.isDebugLevel() and log.debug( storageType + " transformed to " + rootUrl ) 917 918 ucbService = "com.sun.star.ucb.SimpleFileAccess" 919 sfa = ctx.ServiceManager.createInstanceWithContext( ucbService, ctx ) 920 if not sfa: 921 log.debug("PythonScriptProvider couldn't instantiate " +ucbService) 922 raise RuntimeException( 923 "PythonScriptProvider couldn't instantiate " +ucbService, self) 924 self.provCtx = ProviderContext( 925 storageType, sfa, urlHelper, ScriptContext( uno.getComponentContext(), doc, inv ) ) 926 if isPackage: 927 mapPackageName2Path = getPackageName2PathMap( sfa, storageType ) 928 self.provCtx.setPackageAttributes( mapPackageName2Path , rootUrl ) 929 self.dirBrowseNode = PackageBrowseNode( self.provCtx, LANGUAGENAME, rootUrl ) 930 else: 931 self.dirBrowseNode = DirBrowseNode( self.provCtx, LANGUAGENAME, rootUrl ) 932 933 except Exception, e: 934 text = lastException2String() 935 log.debug( "PythonScriptProvider could not be instantiated because of : " + text ) 936 raise e 937 938 def getName( self ): 939 return self.dirBrowseNode.getName() 940 941 def getChildNodes( self ): 942 return self.dirBrowseNode.getChildNodes() 943 944 def hasChildNodes( self ): 945 return self.dirBrowseNode.hasChildNodes() 946 947 def getType( self ): 948 return self.dirBrowseNode.getType() 949 950 def getScript( self, uri ): 951 log.debug( "DirBrowseNode getScript " + uri + " invoked" ) 952 953 raise IllegalArgumentException( "DirBrowseNode couldn't instantiate script " + uri , self , 0 ) 954 955 def getScript( self, scriptUri ): 956 try: 957 log.isDebugLevel() and log.debug( "getScript " + scriptUri + " invoked") 958 959 storageUri = self.provCtx.getStorageUrlFromPersistentUrl( 960 self.provCtx.uriHelper.getStorageURI(scriptUri) ); 961 log.isDebugLevel() and log.debug( "getScript: storageUri = " + storageUri) 962 fileUri = storageUri[0:storageUri.find( "$" )] 963 funcName = storageUri[storageUri.find( "$" )+1:len(storageUri)] 964 965 mod = self.provCtx.getModuleByUrl( fileUri ) 966 log.isDebugLevel() and log.debug( " got mod " + str(mod) ) 967 968 func = mod.__dict__[ funcName ] 969 970 log.isDebugLevel() and log.debug( "got func " + str( func ) ) 971 return PythonScript( func, mod ) 972 except Exception, e: 973 text = lastException2String() 974 log.error( text ) 975 raise ScriptFrameworkErrorException( text, self, scriptUri, LANGUAGENAME, 0 ) 976 977 978 # XServiceInfo 979 def getSupportedServices( self ): 980 return g_ImplementationHelper.getSupportedServices(g_implName) 981 982 def supportsService( self, ServiceName ): 983 return g_ImplementationHelper.supportsService( g_implName, ServiceName ) 984 985 def getImplementationName(self): 986 return g_implName 987 988 def getByName( self, name ): 989 log.debug( "getByName called" + str( name )) 990 return None 991 992 993 def getElementNames( self ): 994 log.debug( "getElementNames called") 995 return () 996 997 def hasByName( self, name ): 998 try: 999 log.debug( "hasByName called " + str( name )) 1000 uri = expandUri(name) 1001 ret = self.provCtx.isUrlInPackage( uri ) 1002 log.debug( "hasByName " + uri + " " +str( ret ) ) 1003 return ret 1004 except Exception, e: 1005 text = lastException2String() 1006 log.debug( "Error in hasByName:" + text ) 1007 return False 1008 1009 def removeByName( self, name ): 1010 log.debug( "removeByName called" + str( name )) 1011 uri = expandUri( name ) 1012 if self.provCtx.isUrlInPackage( uri ): 1013 self.provCtx.removePackageByUrl( uri ) 1014 else: 1015 log.debug( "removeByName unknown uri " + str( name ) + ", ignoring" ) 1016 raise NoSuchElementException( uri + "is not in package" , self ) 1017 log.debug( "removeByName called" + str( uri ) + " successful" ) 1018 1019 def insertByName( self, name, value ): 1020 log.debug( "insertByName called " + str( name ) + " " + str( value )) 1021 uri = expandUri( name ) 1022 if isPyFileInPath( self.provCtx.sfa, uri ): 1023 self.provCtx.addPackageByUrl( uri ) 1024 else: 1025 # package is no python package ... 1026 log.debug( "insertByName: no python files in " + str( uri ) + ", ignoring" ) 1027 raise IllegalArgumentException( uri + " does not contain .py files", self, 1 ) 1028 log.debug( "insertByName called " + str( uri ) + " successful" ) 1029 1030 def replaceByName( self, name, value ): 1031 log.debug( "replaceByName called " + str( name ) + " " + str( value )) 1032 removeByName( name ) 1033 insertByName( name ) 1034 log.debug( "replaceByName called" + str( uri ) + " successful" ) 1035 1036 def getElementType( self ): 1037 log.debug( "getElementType called" ) 1038 return uno.getTypeByName( "void" ) 1039 1040 def hasElements( self ): 1041 log.debug( "hasElements got called") 1042 return False 1043 1044g_ImplementationHelper.addImplementation( \ 1045 PythonScriptProvider,g_implName, \ 1046 ("com.sun.star.script.provider.LanguageScriptProvider", 1047 "com.sun.star.script.provider.ScriptProviderFor"+ LANGUAGENAME,),) 1048 1049 1050log.debug( "pythonscript finished intializing" ) 1051 1052