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#**************************************************************
21import uno
22import unohelper
23import sys
24import imp
25import os
26from com.sun.star.uno import Exception,RuntimeException
27from com.sun.star.loader import XImplementationLoader
28from com.sun.star.lang import XServiceInfo
29
30MODULE_PROTOCOL = "vnd.openoffice.pymodule:"
31DEBUG = 0
32
33g_supportedServices  = "com.sun.star.loader.Python",      # referenced by the native C++ loader !
34g_implementationName = "org.openoffice.comp.pyuno.Loader" # referenced by the native C++ loader !
35
36def splitUrl( url ):
37    nColon = url.find( ":" )
38    if -1 == nColon:
39        raise RuntimeException( "PythonLoader: No protocol in url " + url, None )
40    return url[0:nColon], url[nColon+1:len(url)]
41
42g_loadedComponents = {}
43def checkForPythonPathBesideComponent( url ):
44    path = unohelper.fileUrlToSystemPath( url+"/pythonpath.zip" );
45    if DEBUG == 1:
46        print("checking for existence of " + encfile( path ))
47    if 1 == os.access( encfile( path ), os.F_OK) and not path in sys.path:
48        if DEBUG == 1:
49            print("adding " + encfile( path ) + " to sys.path")
50        sys.path.append( path )
51
52    path = unohelper.fileUrlToSystemPath( url+"/pythonpath" );
53    if 1 == os.access( encfile( path ), os.F_OK) and not path in sys.path:
54        if DEBUG == 1:
55            print("adding " + encfile( path ) + " to sys.path")
56        sys.path.append( path )
57
58def encfile(uni):
59    return uni.encode( sys.getfilesystemencoding())
60
61class Loader( XImplementationLoader, XServiceInfo, unohelper.Base ):
62    def __init__(self, ctx ):
63        if DEBUG:
64            print("pythonloader.Loader ctor")
65        self.ctx = ctx
66
67    def getModuleFromUrl( self, url ):
68        if DEBUG:
69            print("pythonloader: interpreting url " +url)
70        protocol, dependent = splitUrl( url )
71        if "vnd.sun.star.expand" == protocol:
72            exp = self.ctx.getValueByName( "/singletons/com.sun.star.util.theMacroExpander" )
73            url = exp.expandMacros(dependent)
74            protocol,dependent = splitUrl( url )
75
76        if DEBUG:
77            print("pythonloader: after expansion " +protocol +":" + dependent)
78
79        try:
80            if "file" == protocol:
81                # remove \..\ sequence, which may be useful e.g. in the build env
82                url = unohelper.absolutize( url, url )
83
84                # did we load the module already ?
85                mod = g_loadedComponents.get( url )
86                if not mod:
87                    mod = imp.new_module("uno_component")
88
89                    # check for pythonpath.zip beside .py files
90                    checkForPythonPathBesideComponent( url[0:url.rfind('/')] )
91
92                    # read the file
93                    filename = unohelper.fileUrlToSystemPath( url )
94                    fileHandle = file( filename )
95                    src = fileHandle.read().replace("\r","")
96                    if not src.endswith( "\n" ):
97                        src = src + "\n"
98
99                    # compile and execute the module
100                    codeobject = compile( src, encfile(filename), "exec" )
101                    exec(codeobject, mod.__dict__)
102                    mod.__file__ = encfile(filename)
103                    g_loadedComponents[url] = mod
104                return mod
105            elif "vnd.openoffice.pymodule" == protocol:
106                return  __import__( dependent )
107            else:
108                raise RuntimeException( "PythonLoader: Unknown protocol " +
109                                        protocol + " in url " +url, self )
110        except ImportError as e:
111            raise RuntimeException( "Couldn't load "+url+ " for reason "+str(e), None)
112        return None
113
114    def activate( self, implementationName, dummy, locationUrl, regKey ):
115        if DEBUG:
116            print("pythonloader.Loader.activate")
117
118        mod = self.getModuleFromUrl( locationUrl )
119        implHelper = mod.__dict__.get( "g_ImplementationHelper" , None )
120        if implHelper == None:
121            return mod.getComponentFactory( implementationName, self.ctx.ServiceManager, regKey )
122        else:
123            return implHelper.getComponentFactory( implementationName,regKey,self.ctx.ServiceManager)
124
125    def writeRegistryInfo( self, regKey, dummy, locationUrl ):
126        if DEBUG:
127            print("pythonloader.Loader.writeRegistryInfo")
128
129        mod = self.getModuleFromUrl( locationUrl )
130        implHelper = mod.__dict__.get( "g_ImplementationHelper" , None )
131        if implHelper == None:
132            return mod.writeRegistryInfo( self.ctx.ServiceManager, regKey )
133        else:
134            return implHelper.writeRegistryInfo( regKey, self.ctx.ServiceManager )
135
136    def getImplementationName( self ):
137        return g_implementationName
138
139    def supportsService( self, ServiceName ):
140        return ServiceName in self.serviceNames
141
142    def getSupportedServiceNames( self ):
143        return g_supportedServices
144