Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 :
21 : #include <connectivity/predicateinput.hxx>
22 : #include <comphelper/processfactory.hxx>
23 : #include <comphelper/types.hxx>
24 : #include <connectivity/dbtools.hxx>
25 : #include <com/sun/star/i18n/LocaleData.hpp>
26 : #include <com/sun/star/sdbc/DataType.hpp>
27 : #include <com/sun/star/sdbc/ColumnValue.hpp>
28 : #include <com/sun/star/util/NumberFormatter.hpp>
29 : #include <osl/diagnose.h>
30 : #include <connectivity/sqlnode.hxx>
31 : #include <connectivity/PColumn.hxx>
32 : #include <comphelper/numbers.hxx>
33 :
34 : #include <boost/scoped_ptr.hpp>
35 : #include <boost/shared_ptr.hpp>
36 :
37 :
38 : namespace dbtools
39 : {
40 :
41 :
42 : using ::com::sun::star::sdbc::XConnection;
43 : using ::com::sun::star::util::XNumberFormatsSupplier;
44 : using ::com::sun::star::util::NumberFormatter;
45 : using ::com::sun::star::util::XNumberFormatter;
46 : using ::com::sun::star::uno::UNO_QUERY;
47 : using ::com::sun::star::uno::UNO_QUERY_THROW;
48 : using ::com::sun::star::uno::XComponentContext;
49 : using ::com::sun::star::beans::XPropertySet;
50 : using ::com::sun::star::beans::XPropertySetInfo;
51 : using ::com::sun::star::lang::Locale;
52 : using ::com::sun::star::uno::Exception;
53 : using ::com::sun::star::uno::Reference;
54 : using ::com::sun::star::i18n::LocaleData;
55 : using ::com::sun::star::i18n::XLocaleData;
56 : using ::com::sun::star::i18n::LocaleDataItem;
57 : using ::com::sun::star::uno::Any;
58 :
59 : using namespace ::com::sun::star::sdbc;
60 : using namespace ::connectivity;
61 :
62 : using ::connectivity::OSQLParseNode;
63 :
64 :
65 :
66 0 : static sal_Unicode lcl_getSeparatorChar( const OUString& _rSeparator, sal_Unicode _nFallback )
67 : {
68 : OSL_ENSURE( !_rSeparator.isEmpty(), "::lcl_getSeparatorChar: invalid separator string!" );
69 :
70 0 : sal_Unicode nReturn( _nFallback );
71 0 : if ( !_rSeparator.isEmpty() )
72 0 : nReturn = static_cast< sal_Char >( _rSeparator[0] );
73 0 : return nReturn;
74 : }
75 :
76 0 : bool OPredicateInputController::getSeparatorChars( const Locale& _rLocale, sal_Unicode& _rDecSep, sal_Unicode& _rThdSep ) const
77 : {
78 0 : _rDecSep = '.';
79 0 : _rThdSep = ',';
80 : try
81 : {
82 0 : LocaleDataItem aLocaleData;
83 0 : if ( m_xLocaleData.is() )
84 : {
85 0 : aLocaleData = m_xLocaleData->getLocaleItem( _rLocale );
86 0 : _rDecSep = lcl_getSeparatorChar( aLocaleData.decimalSeparator, _rDecSep );
87 0 : _rThdSep = lcl_getSeparatorChar( aLocaleData.decimalSeparator, _rThdSep );
88 0 : return true;
89 0 : }
90 : }
91 0 : catch( const Exception& )
92 : {
93 : OSL_FAIL( "OPredicateInputController::getSeparatorChars: caught an exception!" );
94 : }
95 0 : return false;
96 : }
97 :
98 :
99 4 : OPredicateInputController::OPredicateInputController(
100 : const Reference< XComponentContext >& rxContext, const Reference< XConnection >& _rxConnection, const IParseContext* _pParseContext )
101 : : m_xConnection( _rxConnection )
102 4 : ,m_aParser( rxContext, _pParseContext )
103 : {
104 : try
105 : {
106 : // create a number formatter / number formats supplier pair
107 : OSL_ENSURE( rxContext.is(), "OPredicateInputController::OPredicateInputController: need a service factory!" );
108 4 : if ( rxContext.is() )
109 : {
110 8 : m_xFormatter = Reference< XNumberFormatter >(
111 : NumberFormatter::create(rxContext),
112 : UNO_QUERY_THROW
113 4 : );
114 : }
115 :
116 4 : Reference< XNumberFormatsSupplier > xNumberFormats = ::dbtools::getNumberFormats( m_xConnection, true );
117 4 : if ( !xNumberFormats.is() )
118 0 : ::comphelper::disposeComponent( m_xFormatter );
119 : else
120 4 : m_xFormatter->attachNumberFormatsSupplier( xNumberFormats );
121 :
122 : // create the locale data
123 4 : if ( rxContext.is() )
124 : {
125 4 : m_xLocaleData = LocaleData::create( rxContext );
126 4 : }
127 : }
128 0 : catch( const Exception& )
129 : {
130 : OSL_FAIL( "OPredicateInputController::OPredicateInputController: caught an exception!" );
131 : }
132 4 : }
133 :
134 :
135 36 : OSQLParseNode* OPredicateInputController::implPredicateTree(OUString& _rErrorMessage, const OUString& _rStatement, const Reference< XPropertySet > & _rxField) const
136 : {
137 36 : OSQLParseNode* pReturn = const_cast< OSQLParser& >( m_aParser ).predicateTree( _rErrorMessage, _rStatement, m_xFormatter, _rxField );
138 36 : if ( !pReturn )
139 : { // is it a text field ?
140 0 : sal_Int32 nType = DataType::OTHER;
141 0 : _rxField->getPropertyValue("Type") >>= nType;
142 :
143 0 : if ( ( DataType::CHAR == nType )
144 0 : || ( DataType::VARCHAR == nType )
145 0 : || ( DataType::LONGVARCHAR == nType )
146 0 : || ( DataType::CLOB == nType )
147 : )
148 : { // yes -> force a quoted text and try again
149 0 : OUString sQuoted( _rStatement );
150 0 : if ( !sQuoted.isEmpty()
151 0 : && ( !sQuoted.startsWith("'")
152 0 : || !sQuoted.endsWith("'")
153 : )
154 : )
155 : {
156 : static const char sSingleQuote[] = "'";
157 : static const char sDoubleQuote[] = "''";
158 :
159 0 : sal_Int32 nIndex = -1;
160 0 : sal_Int32 nTemp = 0;
161 0 : while ( -1 != ( nIndex = sQuoted.indexOf( '\'',nTemp ) ) )
162 : {
163 0 : sQuoted = sQuoted.replaceAt( nIndex, 1, sDoubleQuote );
164 0 : nTemp = nIndex+2;
165 : }
166 :
167 0 : OUString sTemp( sSingleQuote );
168 0 : ( sTemp += sQuoted ) += sSingleQuote;
169 0 : sQuoted = sTemp;
170 : }
171 0 : pReturn = const_cast< OSQLParser& >( m_aParser ).predicateTree( _rErrorMessage, sQuoted, m_xFormatter, _rxField );
172 : }
173 :
174 : // one more fallback: for numeric fields, and value strings containing a decimal/thousands separator
175 : // problem which is to be solved with this:
176 : // * a system locale "german"
177 : // * a column formatted with an english number format
178 : // => the output is german (as we use the system locale for this), i.e. "3,4"
179 : // => the input does not recognize the german text, as predicateTree uses the number format
180 : // of the column to determine the main locale - the locale on the context is only a fallback
181 0 : if ( ( DataType::FLOAT == nType )
182 0 : || ( DataType::REAL == nType )
183 0 : || ( DataType::DOUBLE == nType )
184 0 : || ( DataType::NUMERIC == nType )
185 0 : || ( DataType::DECIMAL == nType )
186 : )
187 : {
188 0 : const IParseContext& rParseContext = m_aParser.getContext();
189 : // get the separators for the locale of our parse context
190 : sal_Unicode nCtxDecSep;
191 : sal_Unicode nCtxThdSep;
192 0 : getSeparatorChars( rParseContext.getPreferredLocale(), nCtxDecSep, nCtxThdSep );
193 :
194 : // determine the locale of the column we're building a predicate string for
195 0 : sal_Unicode nFmtDecSep( nCtxDecSep );
196 0 : sal_Unicode nFmtThdSep( nCtxThdSep );
197 : try
198 : {
199 0 : Reference< XPropertySetInfo > xPSI( _rxField->getPropertySetInfo() );
200 0 : if ( xPSI.is() && xPSI->hasPropertyByName("FormatKey") )
201 : {
202 0 : sal_Int32 nFormatKey = 0;
203 0 : _rxField->getPropertyValue("FormatKey") >>= nFormatKey;
204 0 : if ( nFormatKey && m_xFormatter.is() )
205 : {
206 0 : Locale aFormatLocale;
207 : ::comphelper::getNumberFormatProperty(
208 : m_xFormatter,
209 : nFormatKey,
210 : OUString( "Locale" )
211 0 : ) >>= aFormatLocale;
212 :
213 : // valid locale
214 0 : if ( !aFormatLocale.Language.isEmpty() )
215 : {
216 0 : getSeparatorChars( aFormatLocale, nFmtDecSep, nCtxThdSep );
217 0 : }
218 : }
219 0 : }
220 : }
221 0 : catch( const Exception& )
222 : {
223 : OSL_FAIL( "OPredicateInputController::implPredicateTree: caught an exception while dealing with the formats!" );
224 : }
225 :
226 0 : bool bDecDiffers = ( nCtxDecSep != nFmtDecSep );
227 0 : bool bFmtDiffers = ( nCtxThdSep != nFmtThdSep );
228 0 : if ( bDecDiffers || bFmtDiffers )
229 : { // okay, at least one differs
230 : // "translate" the value into the "format locale"
231 0 : OUString sTranslated( _rStatement );
232 0 : const sal_Unicode nIntermediate( '_' );
233 0 : sTranslated = sTranslated.replace( nCtxDecSep, nIntermediate );
234 0 : sTranslated = sTranslated.replace( nCtxThdSep, nFmtThdSep );
235 0 : sTranslated = sTranslated.replace( nIntermediate, nFmtDecSep );
236 :
237 0 : pReturn = const_cast< OSQLParser& >( m_aParser ).predicateTree( _rErrorMessage, sTranslated, m_xFormatter, _rxField );
238 : }
239 : }
240 : }
241 36 : return pReturn;
242 : }
243 :
244 :
245 0 : bool OPredicateInputController::normalizePredicateString(
246 : OUString& _rPredicateValue, const Reference< XPropertySet > & _rxField, OUString* _pErrorMessage ) const
247 : {
248 : OSL_ENSURE( m_xConnection.is() && m_xFormatter.is() && _rxField.is(),
249 : "OPredicateInputController::normalizePredicateString: invalid state or params!" );
250 :
251 0 : bool bSuccess = false;
252 0 : if ( m_xConnection.is() && m_xFormatter.is() && _rxField.is() )
253 : {
254 : // parse the string
255 0 : OUString sError;
256 0 : OUString sTransformedText( _rPredicateValue );
257 0 : OSQLParseNode* pParseNode = implPredicateTree( sError, sTransformedText, _rxField );
258 0 : if ( _pErrorMessage ) *_pErrorMessage = sError;
259 :
260 0 : if ( pParseNode )
261 : {
262 0 : const IParseContext& rParseContext = m_aParser.getContext();
263 : sal_Unicode nDecSeparator, nThousandSeparator;
264 0 : getSeparatorChars( rParseContext.getPreferredLocale(), nDecSeparator, nThousandSeparator );
265 :
266 : // translate it back into a string
267 0 : sTransformedText.clear();
268 : pParseNode->parseNodeToPredicateStr(
269 : sTransformedText, m_xConnection, m_xFormatter, _rxField, OUString(),
270 0 : rParseContext.getPreferredLocale(), (sal_Char)nDecSeparator, &rParseContext
271 0 : );
272 0 : _rPredicateValue = sTransformedText;
273 0 : delete pParseNode;
274 :
275 0 : bSuccess = true;
276 0 : }
277 : }
278 :
279 0 : return bSuccess;
280 : }
281 :
282 :
283 36 : OUString OPredicateInputController::getPredicateValueStr(
284 : const OUString& _rPredicateValue, const Reference< XPropertySet > & _rxField,
285 : OUString* _pErrorMessage ) const
286 : {
287 : OSL_ENSURE( _rxField.is(), "OPredicateInputController::getPredicateValue: invalid params!" );
288 36 : OUString sReturn;
289 36 : if ( _rxField.is() )
290 : {
291 36 : OUString sValue( _rPredicateValue );
292 :
293 : // The following is mostly stolen from the former implementation in the parameter dialog
294 : // (dbaccess/source/ui/dlg/paramdialog.cxx). I do not fully understand this .....
295 :
296 72 : OUString sError;
297 36 : OSQLParseNode* pParseNode = implPredicateTree( sError, sValue, _rxField );
298 36 : if ( _pErrorMessage )
299 0 : *_pErrorMessage = sError;
300 :
301 72 : implParseNode(pParseNode, true) >>= sReturn;
302 : }
303 :
304 36 : return sReturn;
305 : }
306 :
307 0 : OUString OPredicateInputController::getPredicateValueStr(
308 : const OUString& _sField, const OUString& _rPredicateValue, OUString* _pErrorMessage ) const
309 : {
310 0 : OUString sReturn = _rPredicateValue;
311 0 : OUString sError;
312 0 : OUString sField = _sField;
313 0 : sal_Int32 nIndex = 0;
314 0 : sField = sField.getToken(0,'(',nIndex);
315 0 : if(nIndex == -1)
316 0 : sField = _sField;
317 0 : sal_Int32 nType = ::connectivity::OSQLParser::getFunctionReturnType(sField,&m_aParser.getContext());
318 0 : if ( nType == DataType::OTHER || sField.isEmpty() )
319 : {
320 : // first try the international version
321 0 : OUString sSql = "SELECT * FROM x WHERE " + sField + _rPredicateValue;
322 0 : boost::scoped_ptr<OSQLParseNode> pParseNode( const_cast< OSQLParser& >( m_aParser ).parseTree( sError, sSql, true ) );
323 0 : nType = DataType::DOUBLE;
324 0 : if ( pParseNode.get() )
325 : {
326 0 : OSQLParseNode* pColumnRef = pParseNode->getByRule(OSQLParseNode::column_ref);
327 : if ( pColumnRef )
328 : {
329 : }
330 0 : }
331 : }
332 :
333 0 : Reference<XDatabaseMetaData> xMeta = m_xConnection->getMetaData();
334 : parse::OParseColumn* pColumn = new parse::OParseColumn( sField,
335 : OUString(),
336 : OUString(),
337 : OUString(),
338 : ColumnValue::NULLABLE_UNKNOWN,
339 : 0,
340 : 0,
341 : nType,
342 : false,
343 : false,
344 0 : xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers(),
345 : OUString(),
346 : OUString(),
347 0 : OUString());
348 0 : Reference<XPropertySet> xColumn = pColumn;
349 0 : pColumn->setFunction(true);
350 0 : pColumn->setRealName(sField);
351 :
352 0 : OSQLParseNode* pParseNode = implPredicateTree( sError, _rPredicateValue, xColumn );
353 0 : if ( _pErrorMessage )
354 0 : *_pErrorMessage = sError;
355 0 : if(pParseNode)
356 : {
357 0 : implParseNode(pParseNode, true) >>= sReturn;
358 : }
359 0 : return sReturn;
360 : }
361 :
362 0 : Any OPredicateInputController::getPredicateValue(
363 : const OUString& _rPredicateValue, const Reference< XPropertySet > & _rxField,
364 : OUString* _pErrorMessage ) const
365 : {
366 : OSL_ENSURE( _rxField.is(), "OPredicateInputController::getPredicateValue: invalid params!" );
367 :
368 0 : if ( _rxField.is() )
369 : {
370 0 : OUString sValue( _rPredicateValue );
371 :
372 : // The following is mostly stolen from the former implementation in the parameter dialog
373 : // (dbaccess/source/ui/dlg/paramdialog.cxx). I do not fully understand this .....
374 :
375 0 : OUString sError;
376 0 : OSQLParseNode* pParseNode = implPredicateTree( sError, sValue, _rxField );
377 0 : if ( _pErrorMessage )
378 0 : *_pErrorMessage = sError;
379 :
380 0 : return implParseNode(pParseNode, false);
381 : }
382 :
383 0 : return Any();
384 : }
385 :
386 36 : Any OPredicateInputController::implParseNode(OSQLParseNode* pParseNode, bool _bForStatementUse) const
387 : {
388 36 : if ( ! pParseNode )
389 0 : return Any();
390 : else
391 : {
392 36 : OUString sReturn;
393 72 : boost::shared_ptr<OSQLParseNode> xTakeOwnership(pParseNode);
394 36 : OSQLParseNode* pOdbcSpec = pParseNode->getByRule( OSQLParseNode::odbc_fct_spec );
395 36 : if ( pOdbcSpec )
396 : {
397 0 : if ( _bForStatementUse )
398 : {
399 0 : OSQLParseNode* pFuncSpecParent = pOdbcSpec->getParent();
400 : OSL_ENSURE( pFuncSpecParent, "OPredicateInputController::getPredicateValue: an ODBC func spec node without parent?" );
401 0 : if ( pFuncSpecParent )
402 0 : pFuncSpecParent->parseNodeToStr(sReturn, m_xConnection, &m_aParser.getContext(), false, true);
403 : }
404 : else
405 : {
406 0 : OSQLParseNode* pValueNode = pOdbcSpec->getChild(1);
407 0 : if ( SQL_NODE_STRING == pValueNode->getNodeType() )
408 0 : sReturn = pValueNode->getTokenValue();
409 : else
410 0 : pValueNode->parseNodeToStr(sReturn, m_xConnection, &m_aParser.getContext(), false, true);
411 : }
412 : }
413 : else
414 : {
415 36 : if (pParseNode->getKnownRuleID() == OSQLParseNode::test_for_null )
416 : {
417 : assert(pParseNode->count() == 2);
418 0 : return Any();
419 : }
420 : // LEM this seems overly permissive as test...
421 36 : else if (pParseNode->count() >= 3)
422 : {
423 36 : OSQLParseNode* pValueNode = pParseNode->getChild(2);
424 : assert(pValueNode && "OPredicateInputController::getPredicateValue: invalid node child!");
425 36 : if ( !_bForStatementUse )
426 : {
427 0 : if ( SQL_NODE_STRING == pValueNode->getNodeType() )
428 0 : sReturn = pValueNode->getTokenValue();
429 : else
430 : pValueNode->parseNodeToStr(
431 0 : sReturn, m_xConnection, &m_aParser.getContext(), false, true
432 0 : );
433 : }
434 : else
435 : pValueNode->parseNodeToStr(
436 36 : sReturn, m_xConnection, &m_aParser.getContext(), false, true
437 36 : );
438 : }
439 : else
440 : {
441 : OSL_FAIL( "OPredicateInputController::getPredicateValue: unknown/invalid structure (noodbc)!" );
442 0 : return Any();
443 : }
444 : }
445 72 : return Any(sReturn);
446 : }
447 : }
448 :
449 : } // namespace dbtools
450 :
451 :
452 :
453 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|