xref: /trunk/main/cppu/source/uno/EnvStack.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #include "uno/environment.hxx"
29 
30 #include "cppu/EnvDcp.hxx"
31 #include "cppu/Enterable.hxx"
32 
33 #include "osl/thread.h"
34 #include "osl/mutex.hxx"
35 
36 #include <hash_map>
37 
38 
39 using namespace com::sun::star;
40 
41 
42 struct SAL_DLLPRIVATE oslThreadIdentifier_equal
43 {
44     bool operator()(oslThreadIdentifier s1, oslThreadIdentifier s2) const;
45 };
46 
47 bool oslThreadIdentifier_equal::operator()(oslThreadIdentifier s1, oslThreadIdentifier s2) const
48 {
49     bool result = s1 == s2;
50 
51     return result;
52 }
53 
54 
55 struct SAL_DLLPRIVATE oslThreadIdentifier_hash
56 {
57     size_t operator()(oslThreadIdentifier s1) const;
58 };
59 
60 size_t oslThreadIdentifier_hash::operator()(oslThreadIdentifier s1) const
61 {
62     return s1;
63 }
64 
65 typedef ::std::hash_map<oslThreadIdentifier,
66                         uno_Environment *,
67                         oslThreadIdentifier_hash,
68                         oslThreadIdentifier_equal>  ThreadMap;
69 
70 static osl::Mutex s_threadMap_mutex;
71 static ThreadMap  s_threadMap;
72 
73 
74 static rtl::OUString s_uno_envDcp(RTL_CONSTASCII_USTRINGPARAM(UNO_LB_UNO));
75 
76 static void s_setCurrent(uno_Environment * pEnv)
77 {
78     oslThreadIdentifier threadId = osl_getThreadIdentifier(NULL);
79 
80     osl::MutexGuard guard(s_threadMap_mutex);
81     if (pEnv)
82         s_threadMap[threadId] = pEnv;
83 
84     else
85     {
86         ThreadMap::iterator iEnv = s_threadMap.find(threadId);
87         s_threadMap.erase(iEnv);
88     }
89 }
90 
91 static uno_Environment * s_getCurrent(void)
92 {
93     uno_Environment * pEnv = NULL;
94 
95     oslThreadIdentifier threadId = osl_getThreadIdentifier(NULL);
96 
97     osl::MutexGuard guard(s_threadMap_mutex);
98     ThreadMap::iterator iEnv = s_threadMap.find(threadId);
99     if(iEnv != s_threadMap.end())
100         pEnv = iEnv->second;
101 
102     return pEnv;
103 }
104 
105 
106 extern "C" void SAL_CALL uno_getCurrentEnvironment(uno_Environment ** ppEnv, rtl_uString * pTypeName)
107     SAL_THROW_EXTERN_C()
108 {
109     if (*ppEnv)
110     {
111         (*ppEnv)->release(*ppEnv);
112         *ppEnv = NULL;
113     }
114 
115     rtl::OUString currPurpose;
116 
117     uno_Environment * pCurrEnv = s_getCurrent();
118     if (pCurrEnv) // no environment means no purpose
119         currPurpose = cppu::EnvDcp::getPurpose(pCurrEnv->pTypeName);
120 
121     if (pTypeName && rtl_uString_getLength(pTypeName))
122     {
123         rtl::OUString envDcp(pTypeName);
124         envDcp += currPurpose;
125 
126         uno_getEnvironment(ppEnv, envDcp.pData, NULL);
127     }
128     else
129     {
130         if (pCurrEnv)
131         {
132             *ppEnv = pCurrEnv;
133             (*ppEnv)->acquire(*ppEnv);
134         }
135         else
136             uno_getEnvironment(ppEnv, s_uno_envDcp.pData, NULL);
137 
138     }
139 }
140 
141 static rtl::OUString s_getPrefix(rtl::OUString const & str1, rtl::OUString const & str2)
142 {
143     sal_Int32 nIndex1 = 0;
144     sal_Int32 nIndex2 = 0;
145     sal_Int32 sim = 0;
146 
147     rtl::OUString token1;
148     rtl::OUString token2;
149 
150     do
151     {
152         token1 = str1.getToken(0, ':', nIndex1);
153         token2 = str2.getToken(0, ':', nIndex2);
154 
155         if (token1.equals(token2))
156             sim += token1.getLength() + 1;
157     }
158     while(nIndex1 == nIndex2 && nIndex1 >= 0 && token1.equals(token2));
159 
160     rtl::OUString result;
161 
162     if (sim)
163         result = str1.copy(0, sim - 1);
164 
165     return result;
166 }
167 
168 static int s_getNextEnv(uno_Environment ** ppEnv, uno_Environment * pCurrEnv, uno_Environment * pTargetEnv)
169 {
170     int res = 0;
171 
172     rtl::OUString nextPurpose;
173 
174     rtl::OUString currPurpose;
175     if (pCurrEnv)
176         currPurpose = cppu::EnvDcp::getPurpose(pCurrEnv->pTypeName);
177 
178     rtl::OUString targetPurpose;
179     if (pTargetEnv)
180         targetPurpose = cppu::EnvDcp::getPurpose(pTargetEnv->pTypeName);
181 
182     rtl::OUString intermPurpose(s_getPrefix(currPurpose, targetPurpose));
183     if (currPurpose.getLength() > intermPurpose.getLength())
184     {
185         sal_Int32 idx = currPurpose.lastIndexOf(':');
186         nextPurpose = currPurpose.copy(0, idx);
187 
188         res = -1;
189     }
190     else if (intermPurpose.getLength() < targetPurpose.getLength())
191     {
192         sal_Int32 idx = targetPurpose.indexOf(':', intermPurpose.getLength() + 1);
193         if (idx == -1)
194             nextPurpose = targetPurpose;
195 
196         else
197             nextPurpose = targetPurpose.copy(0, idx);
198 
199         res = 1;
200     }
201 
202     if (nextPurpose.getLength())
203     {
204         rtl::OUString next_envDcp(s_uno_envDcp);
205         next_envDcp += nextPurpose;
206 
207         uno_getEnvironment(ppEnv, next_envDcp.pData, NULL);
208     }
209     else
210     {
211         if (*ppEnv)
212             (*ppEnv)->release(*ppEnv);
213 
214         *ppEnv = NULL;
215     }
216 
217     return res;
218 }
219 
220 extern "C" { static void s_pull(va_list * pParam)
221 {
222     uno_EnvCallee * pCallee = va_arg(*pParam, uno_EnvCallee *);
223     va_list       * pXparam = va_arg(*pParam, va_list *);
224 
225     pCallee(pXparam);
226 }}
227 
228 static void s_callInto_v(uno_Environment * pEnv, uno_EnvCallee * pCallee, va_list * pParam)
229 {
230     cppu::Enterable * pEnterable = reinterpret_cast<cppu::Enterable *>(pEnv->pReserved);
231     if (pEnterable)
232         pEnterable->callInto(s_pull, pCallee, pParam);
233 
234     else
235         pCallee(pParam);
236 }
237 
238 static void s_callInto(uno_Environment * pEnv, uno_EnvCallee * pCallee, ...)
239 {
240     va_list param;
241 
242     va_start(param, pCallee);
243     s_callInto_v(pEnv, pCallee, &param);
244     va_end(param);
245 }
246 
247 static void s_callOut_v(uno_Environment * pEnv, uno_EnvCallee * pCallee, va_list * pParam)
248 {
249     cppu::Enterable * pEnterable = reinterpret_cast<cppu::Enterable *>(pEnv->pReserved);
250     if (pEnterable)
251         pEnterable->callOut_v(pCallee, pParam);
252 
253     else
254         pCallee(pParam);
255 }
256 
257 static void s_callOut(uno_Environment * pEnv, uno_EnvCallee * pCallee, ...)
258 {
259     va_list param;
260 
261     va_start(param, pCallee);
262     s_callOut_v(pEnv, pCallee, &param);
263     va_end(param);
264 }
265 
266 static void s_environment_invoke_v(uno_Environment *, uno_Environment *, uno_EnvCallee *, va_list *);
267 
268 extern "C" { static void s_environment_invoke_vv(va_list * pParam)
269 {
270     uno_Environment * pCurrEnv    = va_arg(*pParam, uno_Environment *);
271     uno_Environment * pTargetEnv  = va_arg(*pParam, uno_Environment *);
272     uno_EnvCallee   * pCallee     = va_arg(*pParam, uno_EnvCallee *);
273     va_list         * pXparam     = va_arg(*pParam, va_list *);
274 
275     s_environment_invoke_v(pCurrEnv, pTargetEnv, pCallee, pXparam);
276 }}
277 
278 static void s_environment_invoke_v(uno_Environment * pCurrEnv, uno_Environment * pTargetEnv, uno_EnvCallee * pCallee, va_list * pParam)
279 {
280     uno_Environment * pNextEnv = NULL;
281     switch(s_getNextEnv(&pNextEnv, pCurrEnv, pTargetEnv))
282     {
283     case -1:
284         s_setCurrent(pNextEnv);
285         s_callOut(pCurrEnv, s_environment_invoke_vv, pNextEnv, pTargetEnv, pCallee, pParam);
286         s_setCurrent(pCurrEnv);
287         break;
288 
289     case 0: {
290         uno_Environment * hld = s_getCurrent();
291         s_setCurrent(pCurrEnv);
292         pCallee(pParam);
293         s_setCurrent(hld);
294     }
295         break;
296 
297     case 1:
298         s_setCurrent(pNextEnv);
299         s_callInto(pNextEnv, s_environment_invoke_vv, pNextEnv, pTargetEnv, pCallee, pParam);
300         s_setCurrent(pCurrEnv);
301         break;
302     }
303 
304     if (pNextEnv)
305         pNextEnv->release(pNextEnv);
306 }
307 
308 extern "C" void SAL_CALL uno_Environment_invoke_v(uno_Environment * pTargetEnv, uno_EnvCallee * pCallee, va_list * pParam)
309     SAL_THROW_EXTERN_C()
310 {
311     s_environment_invoke_v(s_getCurrent(), pTargetEnv, pCallee, pParam);
312 }
313 
314 extern "C" void SAL_CALL uno_Environment_invoke(uno_Environment * pEnv, uno_EnvCallee * pCallee, ...)
315     SAL_THROW_EXTERN_C()
316 {
317     va_list param;
318 
319     va_start(param, pCallee);
320     uno_Environment_invoke_v(pEnv, pCallee, &param);
321     va_end(param);
322 }
323 
324 extern "C" void SAL_CALL uno_Environment_enter(uno_Environment * pTargetEnv)
325     SAL_THROW_EXTERN_C()
326 {
327     uno_Environment * pNextEnv = NULL;
328     uno_Environment * pCurrEnv = s_getCurrent();
329 
330     int res;
331     while ( (res = s_getNextEnv(&pNextEnv, pCurrEnv, pTargetEnv)) != 0)
332     {
333         cppu::Enterable * pEnterable;
334 
335         switch(res)
336         {
337         case -1:
338             pEnterable = reinterpret_cast<cppu::Enterable *>(pCurrEnv->pReserved);
339             if (pEnterable)
340                 pEnterable->leave();
341             pCurrEnv->release(pCurrEnv);
342             break;
343 
344         case 1:
345             pNextEnv->acquire(pNextEnv);
346             pEnterable = reinterpret_cast<cppu::Enterable *>(pNextEnv->pReserved);
347             if (pEnterable)
348                 pEnterable->enter();
349             break;
350         }
351 
352         s_setCurrent(pNextEnv);
353         pCurrEnv = pNextEnv;
354     }
355 }
356 
357 int SAL_CALL uno_Environment_isValid(uno_Environment * pEnv, rtl_uString ** pReason)
358     SAL_THROW_EXTERN_C()
359 {
360     int result = 1;
361 
362     rtl::OUString typeName(cppu::EnvDcp::getTypeName(pEnv->pTypeName));
363     if (typeName.equals(s_uno_envDcp))
364     {
365         cppu::Enterable * pEnterable = reinterpret_cast<cppu::Enterable *>(pEnv->pReserved);
366         if (pEnterable)
367             result = pEnterable->isValid((rtl::OUString *)pReason);
368     }
369     else
370     {
371         rtl::OUString envDcp(s_uno_envDcp);
372         envDcp += cppu::EnvDcp::getPurpose(pEnv->pTypeName);
373 
374         uno::Environment env(envDcp);
375 
376         result = env.isValid((rtl::OUString *)pReason);
377     }
378 
379     return result;
380 }
381