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