/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace dbtools { using ::com::sun::star::sdbc::XConnection; using ::com::sun::star::util::XNumberFormatsSupplier; using ::com::sun::star::util::NumberFormatter; using ::com::sun::star::util::XNumberFormatter; using ::com::sun::star::uno::UNO_QUERY; using ::com::sun::star::uno::UNO_QUERY_THROW; using ::com::sun::star::uno::XComponentContext; 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::uno::Reference; using ::com::sun::star::i18n::LocaleData; using ::com::sun::star::i18n::XLocaleData; using ::com::sun::star::i18n::LocaleDataItem; using namespace ::com::sun::star::sdbc; using namespace ::connectivity; using ::connectivity::OSQLParseNode; static sal_Unicode lcl_getSeparatorChar( const OUString& _rSeparator, sal_Unicode _nFallback ) { OSL_ENSURE( !_rSeparator.isEmpty(), "::lcl_getSeparatorChar: invalid separator string!" ); sal_Unicode nReturn( _nFallback ); if ( !_rSeparator.isEmpty() ) nReturn = static_cast< sal_Char >( _rSeparator[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_FAIL( "OPredicateInputController::getSeparatorChars: caught an exception!" ); } return sal_False; } OPredicateInputController::OPredicateInputController( const Reference< XComponentContext >& rxContext, const Reference< XConnection >& _rxConnection, const IParseContext* _pParseContext ) : m_xConnection( _rxConnection ) ,m_aParser( rxContext, _pParseContext ) { try { // create a number formatter / number formats supplier pair OSL_ENSURE( rxContext.is(), "OPredicateInputController::OPredicateInputController: need a service factory!" ); if ( rxContext.is() ) { m_xFormatter = Reference< XNumberFormatter >( NumberFormatter::create(rxContext), UNO_QUERY_THROW ); } Reference< XNumberFormatsSupplier > xNumberFormats = ::dbtools::getNumberFormats( m_xConnection, sal_True ); if ( !xNumberFormats.is() ) ::comphelper::disposeComponent( m_xFormatter ); else m_xFormatter->attachNumberFormatsSupplier( xNumberFormats ); // create the locale data if ( rxContext.is() ) { m_xLocaleData = LocaleData::create( rxContext ); } } catch( const Exception& ) { OSL_FAIL( "OPredicateInputController::OPredicateInputController: caught an exception!" ); } } OSQLParseNode* OPredicateInputController::implPredicateTree(OUString& _rErrorMessage, const 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("Type") >>= nType; if ( ( DataType::CHAR == nType ) || ( DataType::VARCHAR == nType ) || ( DataType::LONGVARCHAR == nType ) || ( DataType::CLOB == nType ) ) { // yes -> force a quoted text and try again OUString sQuoted( _rStatement ); if ( !sQuoted.isEmpty() && ( !sQuoted.startsWith("'") || !sQuoted.endsWith("'") ) ) { static const OUString sSingleQuote( "'" ); static const OUString sDoubleQuote( "''" ); sal_Int32 nIndex = -1; sal_Int32 nTemp = 0; while ( -1 != ( nIndex = sQuoted.indexOf( '\'',nTemp ) ) ) { sQuoted = sQuoted.replaceAt( nIndex, 1, sDoubleQuote ); nTemp = nIndex+2; } 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("FormatKey") ) { sal_Int32 nFormatKey = 0; _rxField->getPropertyValue("FormatKey") >>= nFormatKey; if ( nFormatKey && m_xFormatter.is() ) { Locale aFormatLocale; ::comphelper::getNumberFormatProperty( m_xFormatter, nFormatKey, OUString( "Locale" ) ) >>= aFormatLocale; // valid locale if ( !aFormatLocale.Language.isEmpty() ) { getSeparatorChars( aFormatLocale, nFmtDecSep, nCtxThdSep ); } } } } catch( const Exception& ) { OSL_FAIL( "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" 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( OUString& _rPredicateValue, const Reference< XPropertySet > & _rxField, 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 OUString sError; 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 = OUString(); pParseNode->parseNodeToPredicateStr( sTransformedText, m_xConnection, m_xFormatter, _rxField, OUString(), rParseContext.getPreferredLocale(), (sal_Char)nDecSeparator, &rParseContext ); _rPredicateValue = sTransformedText; delete pParseNode; bSuccess = sal_True; } } return bSuccess; } OUString OPredicateInputController::getPredicateValue( const OUString& _rPredicateValue, const Reference< XPropertySet > & _rxField, sal_Bool _bForStatementUse, OUString* _pErrorMessage ) const { OSL_ENSURE( _rxField.is(), "OPredicateInputController::getPredicateValue: invalid params!" ); OUString sReturn; if ( _rxField.is() ) { 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.startsWith("'") && sValue.endsWith("'"); // 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 OUString sSingleQuote( "'" ); static const OUString sDoubleQuote( "''" ); 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 ..... OUString sError; OSQLParseNode* pParseNode = implPredicateTree( sError, sValue, _rxField ); if ( _pErrorMessage ) *_pErrorMessage = sError; sReturn = implParseNode(pParseNode,_bForStatementUse); } return sReturn; } OUString OPredicateInputController::getPredicateValue( const OUString& _sField, const OUString& _rPredicateValue, sal_Bool _bForStatementUse, OUString* _pErrorMessage ) const { OUString sReturn = _rPredicateValue; OUString sError; 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.isEmpty() ) { // first try the international version OUString sSql = "SELECT * FROM x WHERE " + sField + _rPredicateValue; boost::scoped_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, OUString(), OUString(), OUString(), ColumnValue::NULLABLE_UNKNOWN, 0, 0, nType, sal_False, sal_False, xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers(), OUString(), OUString(), OUString()); 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; } OUString OPredicateInputController::implParseNode(OSQLParseNode* pParseNode,sal_Bool _bForStatementUse) const { OUString sReturn; if ( pParseNode ) { boost::shared_ptr xTakeOwnership(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(), false, 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(), false, true); } } 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(), false, true ); } else pValueNode->parseNodeToStr( sReturn, m_xConnection, &m_aParser.getContext(), false, true ); } else OSL_FAIL( "OPredicateInputController::getPredicateValue: unknown/invalid structure (noodbc)!" ); } } return sReturn; } } // namespace dbtools /* vim:set shiftwidth=4 softtabstop=4 expandtab: */