/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_connectivity.hxx" #include #include #include #include #include #include #include #include #include //......................................................................... namespace dbtools { //......................................................................... using ::com::sun::star::sdbc::XConnection; using ::com::sun::star::lang::XMultiServiceFactory; using ::com::sun::star::util::XNumberFormatsSupplier; using ::com::sun::star::util::XNumberFormatter; using ::com::sun::star::uno::UNO_QUERY; using ::com::sun::star::beans::XPropertySet; using ::com::sun::star::beans::XPropertySetInfo; using ::com::sun::star::lang::Locale; using ::com::sun::star::uno::Exception; using ::com::sun::star::i18n::XLocaleData; using ::com::sun::star::i18n::LocaleDataItem; using namespace ::com::sun::star::sdbc; using namespace ::connectivity; using ::connectivity::OSQLParseNode; #define Reference ::com::sun::star::uno::Reference //===================================================================== //--------------------------------------------------------------------- static sal_Unicode lcl_getSeparatorChar( const ::rtl::OUString& _rSeparator, sal_Unicode _nFallback ) { OSL_ENSURE( 0 < _rSeparator.getLength(), "::lcl_getSeparatorChar: invalid separator string!" ); sal_Unicode nReturn( _nFallback ); if ( _rSeparator.getLength() ) nReturn = static_cast< sal_Char >( _rSeparator.getStr()[0] ); return nReturn; } //===================================================================== //= OPredicateInputController //===================================================================== //--------------------------------------------------------------------- sal_Bool OPredicateInputController::getSeparatorChars( const Locale& _rLocale, sal_Unicode& _rDecSep, sal_Unicode& _rThdSep ) const { _rDecSep = '.'; _rThdSep = ','; try { LocaleDataItem aLocaleData; if ( m_xLocaleData.is() ) { aLocaleData = m_xLocaleData->getLocaleItem( _rLocale ); _rDecSep = lcl_getSeparatorChar( aLocaleData.decimalSeparator, _rDecSep ); _rThdSep = lcl_getSeparatorChar( aLocaleData.decimalSeparator, _rThdSep ); return sal_True; } } catch( const Exception& ) { OSL_ENSURE( sal_False, "OPredicateInputController::getSeparatorChars: caught an exception!" ); } return sal_False; } //--------------------------------------------------------------------- OPredicateInputController::OPredicateInputController( const Reference< XMultiServiceFactory >& _rxORB, const Reference< XConnection >& _rxConnection, const IParseContext* _pParseContext ) :m_xORB( _rxORB ) ,m_xConnection( _rxConnection ) ,m_aParser( m_xORB, _pParseContext ) { try { // create a number formatter / number formats supplier pair OSL_ENSURE( m_xORB.is(), "OPredicateInputController::OPredicateInputController: need a service factory!" ); if ( m_xORB.is() ) { m_xFormatter = Reference< XNumberFormatter >( m_xORB->createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.NumberFormatter" ) ) ), UNO_QUERY ); } Reference< XNumberFormatsSupplier > xNumberFormats = ::dbtools::getNumberFormats( m_xConnection, sal_True ); if ( !xNumberFormats.is() ) ::comphelper::disposeComponent( m_xFormatter ); else if ( m_xFormatter.is() ) m_xFormatter->attachNumberFormatsSupplier( xNumberFormats ); // create the locale data if ( m_xORB.is() ) { m_xLocaleData = m_xLocaleData.query( m_xORB->createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.i18n.LocaleData" ) ) ) ); } } catch( const Exception& ) { OSL_ENSURE( sal_False, "OPredicateInputController::OPredicateInputController: caught an exception!" ); } } //--------------------------------------------------------------------- OSQLParseNode* OPredicateInputController::implPredicateTree(::rtl::OUString& _rErrorMessage, const ::rtl::OUString& _rStatement, const Reference< XPropertySet > & _rxField) const { OSQLParseNode* pReturn = const_cast< OSQLParser& >( m_aParser ).predicateTree( _rErrorMessage, _rStatement, m_xFormatter, _rxField ); if ( !pReturn ) { // is it a text field ? sal_Int32 nType = DataType::OTHER; _rxField->getPropertyValue( ::rtl::OUString::createFromAscii( "Type" ) ) >>= nType; if ( ( DataType::CHAR == nType ) || ( DataType::VARCHAR == nType ) || ( DataType::LONGVARCHAR == nType ) || ( DataType::CLOB == nType ) ) { // yes -> force a quoted text and try again ::rtl::OUString sQuoted( _rStatement ); if ( sQuoted.getLength() && ( (sQuoted.getStr()[0] != '\'') || (sQuoted.getStr()[ sQuoted.getLength() - 1 ] != '\'' ) ) ) { static const ::rtl::OUString sSingleQuote( RTL_CONSTASCII_USTRINGPARAM( "'" ) ); static const ::rtl::OUString sDoubleQuote( RTL_CONSTASCII_USTRINGPARAM( "''" ) ); sal_Int32 nIndex = -1; sal_Int32 nTemp = 0; while ( -1 != ( nIndex = sQuoted.indexOf( '\'',nTemp ) ) ) { sQuoted = sQuoted.replaceAt( nIndex, 1, sDoubleQuote ); nTemp = nIndex+2; } ::rtl::OUString sTemp( sSingleQuote ); ( sTemp += sQuoted ) += sSingleQuote; sQuoted = sTemp; } pReturn = const_cast< OSQLParser& >( m_aParser ).predicateTree( _rErrorMessage, sQuoted, m_xFormatter, _rxField ); } // one more fallback: for numeric fields, and value strings containing a decimal/thousands separator // problem which is to be solved with this: // * a system locale "german" // * a column formatted with an english number format // => the output is german (as we use the system locale for this), i.e. "3,4" // => the input does not recognize the german text, as predicateTree uses the number format // of the column to determine the main locale - the locale on the context is only a fallback if ( ( DataType::FLOAT == nType ) || ( DataType::REAL == nType ) || ( DataType::DOUBLE == nType ) || ( DataType::NUMERIC == nType ) || ( DataType::DECIMAL == nType ) ) { const IParseContext& rParseContext = m_aParser.getContext(); // get the separators for the locale of our parse context sal_Unicode nCtxDecSep; sal_Unicode nCtxThdSep; getSeparatorChars( rParseContext.getPreferredLocale(), nCtxDecSep, nCtxThdSep ); // determine the locale of the column we're building a predicate string for sal_Unicode nFmtDecSep( nCtxDecSep ); sal_Unicode nFmtThdSep( nCtxThdSep ); try { Reference< XPropertySetInfo > xPSI( _rxField->getPropertySetInfo() ); if ( xPSI.is() && xPSI->hasPropertyByName( ::rtl::OUString::createFromAscii( "FormatKey" ) ) ) { sal_Int32 nFormatKey = 0; _rxField->getPropertyValue( ::rtl::OUString::createFromAscii( "FormatKey" ) ) >>= nFormatKey; if ( nFormatKey && m_xFormatter.is() ) { Locale aFormatLocale; ::comphelper::getNumberFormatProperty( m_xFormatter, nFormatKey, ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Locale" ) ) ) >>= aFormatLocale; // valid locale if ( aFormatLocale.Language.getLength() ) { getSeparatorChars( aFormatLocale, nFmtDecSep, nCtxThdSep ); } } } } catch( const Exception& ) { OSL_ENSURE( sal_False, "OPredicateInputController::implPredicateTree: caught an exception while dealing with the formats!" ); } sal_Bool bDecDiffers = ( nCtxDecSep != nFmtDecSep ); sal_Bool bFmtDiffers = ( nCtxThdSep != nFmtThdSep ); if ( bDecDiffers || bFmtDiffers ) { // okay, at least one differs // "translate" the value into the "format locale" ::rtl::OUString sTranslated( _rStatement ); const sal_Unicode nIntermediate( '_' ); sTranslated = sTranslated.replace( nCtxDecSep, nIntermediate ); sTranslated = sTranslated.replace( nCtxThdSep, nFmtThdSep ); sTranslated = sTranslated.replace( nIntermediate, nFmtDecSep ); pReturn = const_cast< OSQLParser& >( m_aParser ).predicateTree( _rErrorMessage, sTranslated, m_xFormatter, _rxField ); } } } return pReturn; } //--------------------------------------------------------------------- sal_Bool OPredicateInputController::normalizePredicateString( ::rtl::OUString& _rPredicateValue, const Reference< XPropertySet > & _rxField, ::rtl::OUString* _pErrorMessage ) const { OSL_ENSURE( m_xConnection.is() && m_xFormatter.is() && _rxField.is(), "OPredicateInputController::normalizePredicateString: invalid state or params!" ); sal_Bool bSuccess = sal_False; if ( m_xConnection.is() && m_xFormatter.is() && _rxField.is() ) { // parse the string ::rtl::OUString sError; ::rtl::OUString sTransformedText( _rPredicateValue ); OSQLParseNode* pParseNode = implPredicateTree( sError, sTransformedText, _rxField ); if ( _pErrorMessage ) *_pErrorMessage = sError; if ( pParseNode ) { const IParseContext& rParseContext = m_aParser.getContext(); sal_Unicode nDecSeparator, nThousandSeparator; getSeparatorChars( rParseContext.getPreferredLocale(), nDecSeparator, nThousandSeparator ); // translate it back into a string sTransformedText = ::rtl::OUString(); pParseNode->parseNodeToPredicateStr( sTransformedText, m_xConnection, m_xFormatter, _rxField, rParseContext.getPreferredLocale(), (sal_Char)nDecSeparator, &rParseContext ); _rPredicateValue = sTransformedText; delete pParseNode; bSuccess = sal_True; } } return bSuccess; } //--------------------------------------------------------------------- ::rtl::OUString OPredicateInputController::getPredicateValue( const ::rtl::OUString& _rPredicateValue, const Reference< XPropertySet > & _rxField, sal_Bool _bForStatementUse, ::rtl::OUString* _pErrorMessage ) const { OSL_ENSURE( _rxField.is(), "OPredicateInputController::getPredicateValue: invalid params!" ); ::rtl::OUString sReturn; if ( _rxField.is() ) { ::rtl::OUString sValue( _rPredicateValue ); // a little problem : if the field is a text field, the normalizePredicateString added two // '-characters to the text. If we would give this to predicateTree this would add // two additional '-characters which we don't want. So check the field format. // FS - 06.01.00 - 71532 sal_Bool bValidQuotedText = ( sValue.getLength() >= 2 ) && ( sValue.getStr()[0] == '\'' ) && ( sValue.getStr()[ sValue.getLength() - 1 ] == '\'' ); // again : as normalizePredicateString always did a conversion on the value text, // bValidQuotedText == sal_True implies that we have a text field, as no other field // values will be formatted with the quote characters if ( bValidQuotedText ) { sValue = sValue.copy( 1, sValue.getLength() - 2 ); static const ::rtl::OUString sSingleQuote( RTL_CONSTASCII_USTRINGPARAM( "'" ) ); static const ::rtl::OUString sDoubleQuote( RTL_CONSTASCII_USTRINGPARAM( "''" ) ); sal_Int32 nIndex = -1; sal_Int32 nTemp = 0; while ( -1 != ( nIndex = sValue.indexOf( sDoubleQuote,nTemp ) ) ) { sValue = sValue.replaceAt( nIndex, 2, sSingleQuote ); nTemp = nIndex+2; } } // The following is mostly stolen from the former implementation in the parameter dialog // (dbaccess/source/ui/dlg/paramdialog.cxx). I do not fully understand this ..... ::rtl::OUString sError; OSQLParseNode* pParseNode = implPredicateTree( sError, sValue, _rxField ); if ( _pErrorMessage ) *_pErrorMessage = sError; sReturn = implParseNode(pParseNode,_bForStatementUse); } return sReturn; } ::rtl::OUString OPredicateInputController::getPredicateValue( const ::rtl::OUString& _sField, const ::rtl::OUString& _rPredicateValue, sal_Bool _bForStatementUse, ::rtl::OUString* _pErrorMessage ) const { ::rtl::OUString sReturn = _rPredicateValue; ::rtl::OUString sError; ::rtl::OUString sField = _sField; sal_Int32 nIndex = 0; sField = sField.getToken(0,'(',nIndex); if(nIndex == -1) sField = _sField; sal_Int32 nType = ::connectivity::OSQLParser::getFunctionReturnType(sField,&m_aParser.getContext()); if ( nType == DataType::OTHER || !sField.getLength() ) { // first try the international version ::rtl::OUString sSql; sSql += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("SELECT * ")); sSql += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(" FROM x WHERE ")); sSql += sField; sSql += _rPredicateValue; ::std::auto_ptr pParseNode( const_cast< OSQLParser& >( m_aParser ).parseTree( sError, sSql, sal_True ) ); nType = DataType::DOUBLE; if ( pParseNode.get() ) { OSQLParseNode* pColumnRef = pParseNode->getByRule(OSQLParseNode::column_ref); if ( pColumnRef ) { } } } Reference xMeta = m_xConnection->getMetaData(); parse::OParseColumn* pColumn = new parse::OParseColumn( sField, ::rtl::OUString(), ::rtl::OUString(), ::rtl::OUString(), ColumnValue::NULLABLE_UNKNOWN, 0, 0, nType, sal_False, sal_False, xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers()); Reference xColumn = pColumn; pColumn->setFunction(sal_True); pColumn->setRealName(sField); OSQLParseNode* pParseNode = implPredicateTree( sError, _rPredicateValue, xColumn ); if ( _pErrorMessage ) *_pErrorMessage = sError; return pParseNode ? implParseNode(pParseNode,_bForStatementUse) : sReturn; } ::rtl::OUString OPredicateInputController::implParseNode(OSQLParseNode* pParseNode,sal_Bool _bForStatementUse) const { ::rtl::OUString sReturn; if ( pParseNode ) { ::std::auto_ptr pTemp(pParseNode); OSQLParseNode* pOdbcSpec = pParseNode->getByRule( OSQLParseNode::odbc_fct_spec ); if ( pOdbcSpec ) { if ( _bForStatementUse ) { OSQLParseNode* pFuncSpecParent = pOdbcSpec->getParent(); OSL_ENSURE( pFuncSpecParent, "OPredicateInputController::getPredicateValue: an ODBC func spec node without parent?" ); if ( pFuncSpecParent ) pFuncSpecParent->parseNodeToStr(sReturn, m_xConnection, &m_aParser.getContext(), sal_False, sal_True); } else { OSQLParseNode* pValueNode = pOdbcSpec->getChild(1); if ( SQL_NODE_STRING == pValueNode->getNodeType() ) sReturn = pValueNode->getTokenValue(); else pValueNode->parseNodeToStr(sReturn, m_xConnection, &m_aParser.getContext(), sal_False, sal_True); // sReturn = pOdbcSpec->getChild(1)->getTokenValue(); } } else { if ( pParseNode->count() >= 3 ) { OSQLParseNode* pValueNode = pParseNode->getChild(2); OSL_ENSURE( pValueNode, "OPredicateInputController::getPredicateValue: invalid node child!" ); if ( !_bForStatementUse ) { if ( SQL_NODE_STRING == pValueNode->getNodeType() ) sReturn = pValueNode->getTokenValue(); else pValueNode->parseNodeToStr( sReturn, m_xConnection, &m_aParser.getContext(), sal_False, sal_True ); } else pValueNode->parseNodeToStr( sReturn, m_xConnection, &m_aParser.getContext(), sal_False, sal_True ); } else OSL_ENSURE( sal_False, "OPredicateInputController::getPredicateValue: unknown/invalid structure (noodbc)!" ); } } return sReturn; } //......................................................................... } // namespace dbtools //.........................................................................