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