xref: /aoo41x/main/sccomp/source/solver/solver.cxx (revision cdf0e10c)
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 #undef LANGUAGE_NONE
29 #define WINAPI __stdcall
30 #define LoadInverseLib FALSE
31 #define LoadLanguageLib FALSE
32 #include <lpsolve/lp_lib.h>
33 #undef LANGUAGE_NONE
34 
35 #include "solver.hxx"
36 #include "solver.hrc"
37 
38 #include <com/sun/star/beans/XPropertySet.hpp>
39 #include <com/sun/star/container/XIndexAccess.hpp>
40 #include <com/sun/star/frame/XModel.hpp>
41 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
42 #include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
43 #include <com/sun/star/sheet/XSpreadsheet.hpp>
44 #include <com/sun/star/table/CellAddress.hpp>
45 #include <com/sun/star/table/CellRangeAddress.hpp>
46 #include <com/sun/star/text/XTextRange.hpp>
47 
48 #include <rtl/math.hxx>
49 #include <rtl/ustrbuf.hxx>
50 #include <cppuhelper/factory.hxx>
51 #include <vector>
52 #include <hash_map>
53 
54 #include <tools/resmgr.hxx>
55 
56 using namespace com::sun::star;
57 
58 using ::rtl::OUString;
59 
60 #define C2U(constAsciiStr) (::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( constAsciiStr ) ))
61 
62 #define STR_NONNEGATIVE   "NonNegative"
63 #define STR_INTEGER       "Integer"
64 #define STR_TIMEOUT       "Timeout"
65 #define STR_EPSILONLEVEL  "EpsilonLevel"
66 #define STR_LIMITBBDEPTH  "LimitBBDepth"
67 
68 // -----------------------------------------------------------------------
69 //  Resources from tools are used for translated strings
70 
71 static ResMgr* pSolverResMgr = NULL;
72 
73 OUString lcl_GetResourceString( sal_uInt32 nId )
74 {
75     if (!pSolverResMgr)
76         pSolverResMgr = CREATEVERSIONRESMGR( solver );
77 
78     return String( ResId( nId, *pSolverResMgr ) );
79 }
80 
81 // -----------------------------------------------------------------------
82 
83 namespace
84 {
85     enum
86     {
87         PROP_NONNEGATIVE,
88         PROP_INTEGER,
89         PROP_TIMEOUT,
90         PROP_EPSILONLEVEL,
91         PROP_LIMITBBDEPTH
92     };
93 }
94 
95 // -----------------------------------------------------------------------
96 
97 // hash map for the coefficients of a dependent cell (objective or constraint)
98 // The size of each vector is the number of columns (variable cells) plus one, first entry is initial value.
99 
100 struct ScSolverCellHash
101 {
102     size_t operator()( const table::CellAddress& rAddress ) const
103     {
104         return ( rAddress.Sheet << 24 ) | ( rAddress.Column << 16 ) | rAddress.Row;
105     }
106 };
107 
108 inline bool AddressEqual( const table::CellAddress& rAddr1, const table::CellAddress& rAddr2 )
109 {
110     return rAddr1.Sheet == rAddr2.Sheet && rAddr1.Column == rAddr2.Column && rAddr1.Row == rAddr2.Row;
111 }
112 
113 struct ScSolverCellEqual
114 {
115     bool operator()( const table::CellAddress& rAddr1, const table::CellAddress& rAddr2 ) const
116     {
117         return AddressEqual( rAddr1, rAddr2 );
118     }
119 };
120 
121 typedef std::hash_map< table::CellAddress, std::vector<double>, ScSolverCellHash, ScSolverCellEqual > ScSolverCellHashMap;
122 
123 // -----------------------------------------------------------------------
124 
125 uno::Reference<table::XCell> lcl_GetCell( const uno::Reference<sheet::XSpreadsheetDocument>& xDoc,
126                                           const table::CellAddress& rPos )
127 {
128     uno::Reference<container::XIndexAccess> xSheets( xDoc->getSheets(), uno::UNO_QUERY );
129     uno::Reference<sheet::XSpreadsheet> xSheet( xSheets->getByIndex( rPos.Sheet ), uno::UNO_QUERY );
130     return xSheet->getCellByPosition( rPos.Column, rPos.Row );
131 }
132 
133 void lcl_SetValue( const uno::Reference<sheet::XSpreadsheetDocument>& xDoc,
134                    const table::CellAddress& rPos, double fValue )
135 {
136     lcl_GetCell( xDoc, rPos )->setValue( fValue );
137 }
138 
139 double lcl_GetValue( const uno::Reference<sheet::XSpreadsheetDocument>& xDoc,
140                      const table::CellAddress& rPos )
141 {
142     return lcl_GetCell( xDoc, rPos )->getValue();
143 }
144 
145 // -------------------------------------------------------------------------
146 
147 SolverComponent::SolverComponent( const uno::Reference<uno::XComponentContext>& /* rSMgr */ ) :
148     OPropertyContainer( GetBroadcastHelper() ),
149     mbMaximize( sal_True ),
150     mbNonNegative( sal_False ),
151     mbInteger( sal_False ),
152     mnTimeout( 100 ),
153     mnEpsilonLevel( 0 ),
154     mbLimitBBDepth( sal_True ),
155     mbSuccess( sal_False ),
156     mfResultValue( 0.0 )
157 {
158     // for XPropertySet implementation:
159     registerProperty( C2U(STR_NONNEGATIVE),  PROP_NONNEGATIVE,  0, &mbNonNegative,  getCppuType( &mbNonNegative )  );
160     registerProperty( C2U(STR_INTEGER),      PROP_INTEGER,      0, &mbInteger,      getCppuType( &mbInteger )      );
161     registerProperty( C2U(STR_TIMEOUT),      PROP_TIMEOUT,      0, &mnTimeout,      getCppuType( &mnTimeout )      );
162     registerProperty( C2U(STR_EPSILONLEVEL), PROP_EPSILONLEVEL, 0, &mnEpsilonLevel, getCppuType( &mnEpsilonLevel ) );
163     registerProperty( C2U(STR_LIMITBBDEPTH), PROP_LIMITBBDEPTH, 0, &mbLimitBBDepth, getCppuType( &mbLimitBBDepth ) );
164 }
165 
166 SolverComponent::~SolverComponent()
167 {
168 }
169 
170 IMPLEMENT_FORWARD_XINTERFACE2( SolverComponent, SolverComponent_Base, OPropertyContainer )
171 IMPLEMENT_FORWARD_XTYPEPROVIDER2( SolverComponent, SolverComponent_Base, OPropertyContainer )
172 
173 cppu::IPropertyArrayHelper* SolverComponent::createArrayHelper() const
174 {
175     uno::Sequence<beans::Property> aProps;
176     describeProperties( aProps );
177     return new cppu::OPropertyArrayHelper( aProps );
178 }
179 
180 cppu::IPropertyArrayHelper& SAL_CALL SolverComponent::getInfoHelper()
181 {
182     return *getArrayHelper();
183 }
184 
185 uno::Reference<beans::XPropertySetInfo> SAL_CALL SolverComponent::getPropertySetInfo() throw(uno::RuntimeException)
186 {
187     return createPropertySetInfo( getInfoHelper() );
188 }
189 
190 // XSolverDescription
191 
192 OUString SAL_CALL SolverComponent::getComponentDescription() throw (uno::RuntimeException)
193 {
194     return lcl_GetResourceString( RID_SOLVER_COMPONENT );
195 }
196 
197 OUString SAL_CALL SolverComponent::getStatusDescription() throw (uno::RuntimeException)
198 {
199     return maStatus;
200 }
201 
202 OUString SAL_CALL SolverComponent::getPropertyDescription( const OUString& rPropertyName ) throw (uno::RuntimeException)
203 {
204     sal_uInt32 nResId = 0;
205 	sal_Int32 nHandle = getInfoHelper().getHandleByName( rPropertyName );
206     switch (nHandle)
207     {
208         case PROP_NONNEGATIVE:
209             nResId = RID_PROPERTY_NONNEGATIVE;
210             break;
211         case PROP_INTEGER:
212             nResId = RID_PROPERTY_INTEGER;
213             break;
214         case PROP_TIMEOUT:
215             nResId = RID_PROPERTY_TIMEOUT;
216             break;
217         case PROP_EPSILONLEVEL:
218             nResId = RID_PROPERTY_EPSILONLEVEL;
219             break;
220         case PROP_LIMITBBDEPTH:
221             nResId = RID_PROPERTY_LIMITBBDEPTH;
222             break;
223         default:
224             {
225                 // unknown - leave empty
226             }
227     }
228     OUString aRet;
229     if ( nResId )
230         aRet = lcl_GetResourceString( nResId );
231     return aRet;
232 }
233 
234 // XSolver: settings
235 
236 uno::Reference<sheet::XSpreadsheetDocument> SAL_CALL SolverComponent::getDocument() throw(uno::RuntimeException)
237 {
238     return mxDoc;
239 }
240 
241 void SAL_CALL SolverComponent::setDocument( const uno::Reference<sheet::XSpreadsheetDocument>& _document )
242                                 throw(uno::RuntimeException)
243 {
244     mxDoc = _document;
245 }
246 
247 table::CellAddress SAL_CALL SolverComponent::getObjective() throw(uno::RuntimeException)
248 {
249     return maObjective;
250 }
251 
252 void SAL_CALL SolverComponent::setObjective( const table::CellAddress& _objective ) throw(uno::RuntimeException)
253 {
254     maObjective = _objective;
255 }
256 
257 uno::Sequence<table::CellAddress> SAL_CALL SolverComponent::getVariables() throw(uno::RuntimeException)
258 {
259     return maVariables;
260 }
261 
262 void SAL_CALL SolverComponent::setVariables( const uno::Sequence<table::CellAddress>& _variables )
263                                 throw(uno::RuntimeException)
264 {
265     maVariables = _variables;
266 }
267 
268 uno::Sequence<sheet::SolverConstraint> SAL_CALL SolverComponent::getConstraints() throw(uno::RuntimeException)
269 {
270     return maConstraints;
271 }
272 
273 void SAL_CALL SolverComponent::setConstraints( const uno::Sequence<sheet::SolverConstraint>& _constraints )
274                                 throw(uno::RuntimeException)
275 {
276     maConstraints = _constraints;
277 }
278 
279 sal_Bool SAL_CALL SolverComponent::getMaximize() throw(uno::RuntimeException)
280 {
281     return mbMaximize;
282 }
283 
284 void SAL_CALL SolverComponent::setMaximize( sal_Bool _maximize ) throw(uno::RuntimeException)
285 {
286     mbMaximize = _maximize;
287 }
288 
289 // XSolver: get results
290 
291 sal_Bool SAL_CALL SolverComponent::getSuccess() throw(uno::RuntimeException)
292 {
293     return mbSuccess;
294 }
295 
296 double SAL_CALL SolverComponent::getResultValue() throw(uno::RuntimeException)
297 {
298     return mfResultValue;
299 }
300 
301 uno::Sequence<double> SAL_CALL SolverComponent::getSolution() throw(uno::RuntimeException)
302 {
303     return maSolution;
304 }
305 
306 // -------------------------------------------------------------------------
307 
308 void SAL_CALL SolverComponent::solve() throw(uno::RuntimeException)
309 {
310     uno::Reference<frame::XModel> xModel( mxDoc, uno::UNO_QUERY );
311     if ( !xModel.is() )
312         throw uno::RuntimeException();
313 
314     maStatus = OUString();
315     mbSuccess = false;
316 
317     if ( mnEpsilonLevel < EPS_TIGHT || mnEpsilonLevel > EPS_BAGGY )
318     {
319         maStatus = lcl_GetResourceString( RID_ERROR_EPSILONLEVEL );
320         return;
321     }
322 
323     xModel->lockControllers();
324 
325     // collect variables in vector (?)
326 
327     std::vector<table::CellAddress> aVariableCells;
328     for (sal_Int32 nPos=0; nPos<maVariables.getLength(); nPos++)
329         aVariableCells.push_back( maVariables[nPos] );
330     size_t nVariables = aVariableCells.size();
331     size_t nVar = 0;
332 
333     // collect all dependent cells
334 
335     ScSolverCellHashMap aCellsHash;
336     aCellsHash[maObjective].reserve( nVariables + 1 );                  // objective function
337 
338     for (sal_Int32 nConstrPos = 0; nConstrPos < maConstraints.getLength(); ++nConstrPos)
339     {
340         table::CellAddress aCellAddr = maConstraints[nConstrPos].Left;
341         aCellsHash[aCellAddr].reserve( nVariables + 1 );                // constraints: left hand side
342 
343         if ( maConstraints[nConstrPos].Right >>= aCellAddr )
344             aCellsHash[aCellAddr].reserve( nVariables + 1 );            // constraints: right hand side
345     }
346 
347     // set all variables to zero
348     //! store old values?
349     //! use old values as initial values?
350     std::vector<table::CellAddress>::const_iterator aVarIter;
351     for ( aVarIter = aVariableCells.begin(); aVarIter != aVariableCells.end(); ++aVarIter )
352     {
353         lcl_SetValue( mxDoc, *aVarIter, 0.0 );
354     }
355 
356     // read initial values from all dependent cells
357     ScSolverCellHashMap::iterator aCellsIter;
358     for ( aCellsIter = aCellsHash.begin(); aCellsIter != aCellsHash.end(); ++aCellsIter )
359     {
360         double fValue = lcl_GetValue( mxDoc, aCellsIter->first );
361         aCellsIter->second.push_back( fValue );                         // store as first element, as-is
362     }
363 
364     // loop through variables
365     for ( aVarIter = aVariableCells.begin(); aVarIter != aVariableCells.end(); ++aVarIter )
366     {
367         lcl_SetValue( mxDoc, *aVarIter, 1.0 );      // set to 1 to examine influence
368 
369         // read value change from all dependent cells
370         for ( aCellsIter = aCellsHash.begin(); aCellsIter != aCellsHash.end(); ++aCellsIter )
371         {
372             double fChanged = lcl_GetValue( mxDoc, aCellsIter->first );
373             double fInitial = aCellsIter->second.front();
374             aCellsIter->second.push_back( fChanged - fInitial );
375         }
376 
377         lcl_SetValue( mxDoc, *aVarIter, 2.0 );      // minimal test for linearity
378 
379         for ( aCellsIter = aCellsHash.begin(); aCellsIter != aCellsHash.end(); ++aCellsIter )
380         {
381             double fInitial = aCellsIter->second.front();
382             double fCoeff   = aCellsIter->second.back();       // last appended: coefficient for this variable
383             double fTwo     = lcl_GetValue( mxDoc, aCellsIter->first );
384 
385             bool bLinear = rtl::math::approxEqual( fTwo, fInitial + 2.0 * fCoeff ) ||
386                            rtl::math::approxEqual( fInitial, fTwo - 2.0 * fCoeff );
387             // second comparison is needed in case fTwo is zero
388             if ( !bLinear )
389                 maStatus = lcl_GetResourceString( RID_ERROR_NONLINEAR );
390         }
391 
392         lcl_SetValue( mxDoc, *aVarIter, 0.0 );      // set back to zero for examining next variable
393     }
394 
395     xModel->unlockControllers();
396 
397     if ( maStatus.getLength() )
398         return;
399 
400     //
401     // build lp_solve model
402     //
403 
404     lprec* lp = make_lp( 0, nVariables );
405     if ( !lp )
406         return;
407 
408     set_outputfile( lp, const_cast<char*>( "" ) );  // no output
409 
410     // set objective function
411 
412     const std::vector<double>& rObjCoeff = aCellsHash[maObjective];
413     REAL* pObjVal = new REAL[nVariables+1];
414     pObjVal[0] = 0.0;                           // ignored
415     for (nVar=0; nVar<nVariables; nVar++)
416         pObjVal[nVar+1] = rObjCoeff[nVar+1];
417     set_obj_fn( lp, pObjVal );
418     delete[] pObjVal;
419     set_rh( lp, 0, rObjCoeff[0] );              // constant term of objective
420 
421     // add rows
422 
423     set_add_rowmode(lp, TRUE);
424 
425     for (sal_Int32 nConstrPos = 0; nConstrPos < maConstraints.getLength(); ++nConstrPos)
426     {
427         // integer constraints are set later
428         sheet::SolverConstraintOperator eOp = maConstraints[nConstrPos].Operator;
429         if ( eOp == sheet::SolverConstraintOperator_LESS_EQUAL ||
430              eOp == sheet::SolverConstraintOperator_GREATER_EQUAL ||
431              eOp == sheet::SolverConstraintOperator_EQUAL )
432         {
433             double fDirectValue = 0.0;
434             bool bRightCell = false;
435             table::CellAddress aRightAddr;
436             const uno::Any& rRightAny = maConstraints[nConstrPos].Right;
437             if ( rRightAny >>= aRightAddr )
438                 bRightCell = true;                  // cell specified as right-hand side
439             else
440                 rRightAny >>= fDirectValue;         // constant value
441 
442             table::CellAddress aLeftAddr = maConstraints[nConstrPos].Left;
443 
444             const std::vector<double>& rLeftCoeff = aCellsHash[aLeftAddr];
445             REAL* pValues = new REAL[nVariables+1];
446             pValues[0] = 0.0;                               // ignored?
447             for (nVar=0; nVar<nVariables; nVar++)
448                 pValues[nVar+1] = rLeftCoeff[nVar+1];
449 
450             // if left hand cell has a constant term, put into rhs value
451             double fRightValue = -rLeftCoeff[0];
452 
453             if ( bRightCell )
454             {
455                 const std::vector<double>& rRightCoeff = aCellsHash[aRightAddr];
456                 // modify pValues with rhs coefficients
457                 for (nVar=0; nVar<nVariables; nVar++)
458                     pValues[nVar+1] -= rRightCoeff[nVar+1];
459 
460                 fRightValue += rRightCoeff[0];      // constant term
461             }
462             else
463                 fRightValue += fDirectValue;
464 
465             int nConstrType = LE;
466             switch ( eOp )
467             {
468                 case sheet::SolverConstraintOperator_LESS_EQUAL:    nConstrType = LE; break;
469                 case sheet::SolverConstraintOperator_GREATER_EQUAL: nConstrType = GE; break;
470                 case sheet::SolverConstraintOperator_EQUAL:         nConstrType = EQ; break;
471                 default:
472                     OSL_ENSURE( false, "unexpected enum type" );
473             }
474             add_constraint( lp, pValues, nConstrType, fRightValue );
475 
476             delete[] pValues;
477         }
478     }
479 
480     set_add_rowmode(lp, FALSE);
481 
482     // apply settings to all variables
483 
484     for (nVar=0; nVar<nVariables; nVar++)
485     {
486         if ( !mbNonNegative )
487             set_unbounded(lp, nVar+1);          // allow negative (default is non-negative)
488                                                 //! collect bounds from constraints?
489         if ( mbInteger )
490             set_int(lp, nVar+1, TRUE);
491     }
492 
493     // apply single-var integer constraints
494 
495     for (sal_Int32 nConstrPos = 0; nConstrPos < maConstraints.getLength(); ++nConstrPos)
496     {
497         sheet::SolverConstraintOperator eOp = maConstraints[nConstrPos].Operator;
498         if ( eOp == sheet::SolverConstraintOperator_INTEGER ||
499              eOp == sheet::SolverConstraintOperator_BINARY )
500         {
501             table::CellAddress aLeftAddr = maConstraints[nConstrPos].Left;
502             // find variable index for cell
503             for (nVar=0; nVar<nVariables; nVar++)
504                 if ( AddressEqual( aVariableCells[nVar], aLeftAddr ) )
505                 {
506                     if ( eOp == sheet::SolverConstraintOperator_INTEGER )
507                         set_int(lp, nVar+1, TRUE);
508                     else
509                         set_binary(lp, nVar+1, TRUE);
510                 }
511         }
512     }
513 
514     if ( mbMaximize )
515         set_maxim(lp);
516     else
517         set_minim(lp);
518 
519     if ( !mbLimitBBDepth )
520         set_bb_depthlimit( lp, 0 );
521 
522     set_epslevel( lp, mnEpsilonLevel );
523     set_timeout( lp, mnTimeout );
524 
525     // solve model
526 
527     int nResult = ::solve( lp );
528 
529     mbSuccess = ( nResult == OPTIMAL );
530     if ( mbSuccess )
531     {
532         // get solution
533 
534         maSolution.realloc( nVariables );
535 
536         REAL* pResultVar = NULL;
537         get_ptr_variables( lp, &pResultVar );
538         for (nVar=0; nVar<nVariables; nVar++)
539             maSolution[nVar] = pResultVar[nVar];
540 
541         mfResultValue = get_objective( lp );
542     }
543     else if ( nResult == INFEASIBLE )
544         maStatus = lcl_GetResourceString( RID_ERROR_INFEASIBLE );
545     else if ( nResult == UNBOUNDED )
546         maStatus = lcl_GetResourceString( RID_ERROR_UNBOUNDED );
547     else if ( nResult == TIMEOUT || nResult == SUBOPTIMAL )
548         maStatus = lcl_GetResourceString( RID_ERROR_TIMEOUT );
549     // SUBOPTIMAL is assumed to be caused by a timeout, and reported as an error
550 
551     delete_lp( lp );
552 }
553 
554 // -------------------------------------------------------------------------
555 
556 // XServiceInfo
557 
558 uno::Sequence< OUString > SolverComponent_getSupportedServiceNames()
559 {
560     uno::Sequence< OUString > aServiceNames( 1 );
561     aServiceNames[ 0 ] = OUString::createFromAscii( "com.sun.star.sheet.Solver" );
562     return aServiceNames;
563 }
564 
565 OUString SolverComponent_getImplementationName()
566 {
567     return OUString::createFromAscii( "com.sun.star.comp.Calc.Solver" );
568 }
569 
570 OUString SAL_CALL SolverComponent::getImplementationName() throw(uno::RuntimeException)
571 {
572     return SolverComponent_getImplementationName();
573 }
574 
575 sal_Bool SAL_CALL SolverComponent::supportsService( const OUString& rServiceName ) throw(uno::RuntimeException)
576 {
577     const uno::Sequence< OUString > aServices = SolverComponent_getSupportedServiceNames();
578     const OUString* pArray = aServices.getConstArray();
579     const OUString* pArrayEnd = pArray + aServices.getLength();
580     return ::std::find( pArray, pArrayEnd, rServiceName ) != pArrayEnd;
581 }
582 
583 uno::Sequence<OUString> SAL_CALL SolverComponent::getSupportedServiceNames() throw(uno::RuntimeException)
584 {
585     return SolverComponent_getSupportedServiceNames();
586 }
587 
588 uno::Reference<uno::XInterface> SolverComponent_createInstance( const uno::Reference<uno::XComponentContext>& rSMgr )
589     throw(uno::Exception)
590 {
591 	return (cppu::OWeakObject*) new SolverComponent( rSMgr );
592 }
593 
594 // -------------------------------------------------------------------------
595 
596 extern "C"
597 {
598     SAL_DLLPUBLIC_EXPORT void SAL_CALL component_getImplementationEnvironment(
599         const sal_Char ** ppEnvTypeName, uno_Environment ** )
600     {
601         *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
602     }
603 
604     // -------------------------------------------------------------------------
605 
606     SAL_DLLPUBLIC_EXPORT void* SAL_CALL component_getFactory( const sal_Char * pImplName, void * pServiceManager, void * /*pRegistryKey*/ )
607     {
608         OUString    aImplName( OUString::createFromAscii( pImplName ) );
609         void*       pRet = 0;
610 
611         if( pServiceManager )
612         {
613             uno::Reference< lang::XSingleComponentFactory > xFactory;
614             if( aImplName.equals( SolverComponent_getImplementationName() ) )
615                 xFactory = cppu::createSingleComponentFactory(
616                         SolverComponent_createInstance,
617                         OUString::createFromAscii( pImplName ),
618                         SolverComponent_getSupportedServiceNames() );
619 
620             if( xFactory.is() )
621             {
622                 xFactory->acquire();
623                 pRet = xFactory.get();
624             }
625         }
626         return pRet;
627     }
628 }
629 
630