summaryrefslogtreecommitdiff
path: root/connectivity/source/commontools/predicateinput.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'connectivity/source/commontools/predicateinput.cxx')
-rw-r--r--connectivity/source/commontools/predicateinput.cxx391
1 files changed, 391 insertions, 0 deletions
diff --git a/connectivity/source/commontools/predicateinput.cxx b/connectivity/source/commontools/predicateinput.cxx
new file mode 100644
index 000000000000..8ffebb0cfbda
--- /dev/null
+++ b/connectivity/source/commontools/predicateinput.cxx
@@ -0,0 +1,391 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_connectivity.hxx"
+#include <connectivity/predicateinput.hxx>
+#include <comphelper/types.hxx>
+#include <connectivity/dbtools.hxx>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <osl/diagnose.h>
+#include <connectivity/sqlnode.hxx>
+#include <comphelper/numbers.hxx>
+
+//.........................................................................
+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;
+
+ if ( pParseNode )
+ {
+ OSQLParseNode* pOdbcSpec = pParseNode->getByRule( OSQLParseNode::odbc_fct_spec );
+ if ( pOdbcSpec )
+ {
+ if ( !_bForStatementUse )
+ {
+ if ( ( pOdbcSpec->count() >= 2 )
+ && ( SQL_NODE_STRING == pOdbcSpec->getChild(1)->getNodeType() )
+ )
+ {
+
+ sReturn = pOdbcSpec->getChild(1)->getTokenValue();
+ }
+ else
+ OSL_ENSURE( sal_False, "OPredicateInputController::getPredicateValue: unknown/invalid structure (odbc + param use)!" );
+ }
+ else
+ {
+ 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
+ {
+ 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)!" );
+ }
+
+ delete pParseNode;
+ }
+ }
+
+ return sReturn;
+ }
+//.........................................................................
+} // namespace dbtools
+//.........................................................................
+
+