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