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 #include <sal/config.h>
21
22 #include <cppuhelper/implbase.hxx>
23 #include <cppuhelper/supportsservice.hxx>
24 #include <com/sun/star/lang/IllegalArgumentException.hpp>
25 #include <com/sun/star/lang/XServiceInfo.hpp>
26 #include <com/sun/star/inspection/XStringRepresentation.hpp>
27 #include <com/sun/star/lang/XInitialization.hpp>
28 #include <com/sun/star/script/CannotConvertException.hpp>
29 #include <com/sun/star/script/XTypeConverter.hpp>
30 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
31 #include <com/sun/star/reflection/XConstantsTypeDescription.hpp>
32 #include <com/sun/star/util/DateTime.hpp>
33 #include <com/sun/star/util/Date.hpp>
34 #include <com/sun/star/util/Time.hpp>
35 #include <com/sun/star/uno/XComponentContext.hpp>
36 #include <connectivity/dbconversion.hxx>
37 #include <osl/diagnose.h>
38 #include <rtl/ustrbuf.hxx>
39 #include <sal/log.hxx>
40 #include <yesno.hrc>
41 #include <comphelper/types.hxx>
42 #include "modulepcr.hxx"
43
44 #include <algorithm>
45
46 namespace pcr{
47
48 using namespace ::com::sun::star;
49 using namespace ::com::sun::star::uno;
50
51 namespace {
52
53 class StringRepresentation:
54 public ::cppu::WeakImplHelper<
55 lang::XServiceInfo,
56 inspection::XStringRepresentation,
57 lang::XInitialization>
58 {
59 public:
60 explicit StringRepresentation(uno::Reference< uno::XComponentContext > const & context);
61 StringRepresentation (const StringRepresentation&) = delete;
62 StringRepresentation& operator=(const StringRepresentation&) = delete;
63
64 // lang::XServiceInfo:
65 virtual OUString SAL_CALL getImplementationName() override;
66 virtual sal_Bool SAL_CALL supportsService(const OUString & ServiceName) override;
67 virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
68
69 // inspection::XStringRepresentation:
70 virtual OUString SAL_CALL convertToControlValue(const uno::Any & PropertyValue) override;
71 virtual uno::Any SAL_CALL convertToPropertyValue(const OUString & ControlValue, const uno::Type & ControlValueType) override;
72
73 // lang::XInitialization:
74 virtual void SAL_CALL initialize(const uno::Sequence< uno::Any > & aArguments) override;
75
76 private:
~StringRepresentation()77 virtual ~StringRepresentation() override {}
78
79 /** converts a generic value into a string representation
80
81 If you want to convert values whose string representation does not depend
82 on a concrete property, use this version
83
84 @return <TRUE/>
85 if and only if the value could be converted
86 */
87 static bool convertGenericValueToString(
88 const uno::Any& _rValue,
89 OUString& _rStringRep
90 );
91
92 /** converts string representation into generic value
93
94 If you want to convert values whose string representation does not depend
95 on a concrete property, use this version
96
97 @return <TRUE/>
98 if and only if the value could be converted
99 */
100 static bool convertStringToGenericValue(
101 const OUString& _rStringRep,
102 uno::Any& _rValue,
103 const uno::Type& _rTargetType
104 );
105
106 /** uses the simple convert method from the type converter
107 *
108 * \param _rValue the value to be converted
109 * \return the converted string.
110 */
111 OUString convertSimpleToString( const uno::Any& _rValue );
112
113 /** converts a string into his constant value if it exists, otherwise the type converter is used.
114 * \param _rValue the value to be converted
115 * \param _ePropertyType the type of the property to be converted into
116 * \return the converted value
117 */
118 uno::Any convertStringToSimple( const OUString& _rValue,const uno::TypeClass& _ePropertyType );
119
120 uno::Reference< uno::XComponentContext > m_xContext;
121 uno::Reference< script::XTypeConverter > m_xTypeConverter;
122 uno::Reference< reflection::XConstantsTypeDescription > m_xTypeDescription;
123 uno::Sequence< OUString > m_aValues;
124 uno::Sequence< uno::Reference< reflection::XConstantTypeDescription> > m_aConstants;
125
126 };
127
128 }
129
StringRepresentation(uno::Reference<uno::XComponentContext> const & context)130 StringRepresentation::StringRepresentation(uno::Reference< uno::XComponentContext > const & context) :
131 m_xContext(context)
132 {}
133
134 // com.sun.star.uno.XServiceInfo:
getImplementationName()135 OUString SAL_CALL StringRepresentation::getImplementationName()
136 {
137 return "StringRepresentation";
138 }
139
supportsService(OUString const & serviceName)140 sal_Bool SAL_CALL StringRepresentation::supportsService(OUString const & serviceName)
141 {
142 return cppu::supportsService(this, serviceName);
143 }
144
getSupportedServiceNames()145 uno::Sequence< OUString > SAL_CALL StringRepresentation::getSupportedServiceNames()
146 {
147 return { "com.sun.star.inspection.StringRepresentation" };
148 }
149
150 // inspection::XStringRepresentation:
convertToControlValue(const uno::Any & PropertyValue)151 OUString SAL_CALL StringRepresentation::convertToControlValue(const uno::Any & PropertyValue)
152 {
153 OUString sReturn;
154 if ( !convertGenericValueToString( PropertyValue, sReturn ) )
155 {
156 sReturn = convertSimpleToString( PropertyValue );
157 #ifdef DBG_UTIL
158 if ( sReturn.isEmpty() && PropertyValue.hasValue() )
159 {
160 SAL_WARN( "extensions.propctrlr", "StringRepresentation::convertPropertyValueToStringRepresentation: cannot convert values of type '"
161 << PropertyValue.getValueType().getTypeName()
162 << "'!" );
163 }
164 #endif
165 }
166
167 return sReturn;
168 }
169
convertToPropertyValue(const OUString & ControlValue,const uno::Type & ControlValueType)170 uno::Any SAL_CALL StringRepresentation::convertToPropertyValue(const OUString & ControlValue, const uno::Type & ControlValueType)
171 {
172 uno::Any aReturn;
173
174 uno::TypeClass ePropertyType = ControlValueType.getTypeClass();
175 switch ( ePropertyType )
176 {
177 case uno::TypeClass_FLOAT:
178 case uno::TypeClass_DOUBLE:
179 case uno::TypeClass_BYTE:
180 case uno::TypeClass_SHORT:
181 case uno::TypeClass_LONG:
182 case uno::TypeClass_HYPER:
183 case uno::TypeClass_UNSIGNED_SHORT:
184 case uno::TypeClass_UNSIGNED_LONG:
185 case uno::TypeClass_UNSIGNED_HYPER:
186 try
187 {
188 aReturn = convertStringToSimple(ControlValue, ePropertyType);
189 }
190 catch( const script::CannotConvertException& ) { }
191 catch( const lang::IllegalArgumentException& ) { }
192 break;
193
194 default:
195 #if OSL_DEBUG_LEVEL > 0
196 bool bCanConvert =
197 #endif
198 convertStringToGenericValue( ControlValue, aReturn, ControlValueType );
199
200 #if OSL_DEBUG_LEVEL > 0
201 // could not convert ...
202 if ( !bCanConvert && !ControlValue.isEmpty() )
203 {
204 SAL_WARN( "extensions.propctrlr", "StringRepresentation::convertStringRepresentationToPropertyValue: cannot convert into values of type '"
205 << ControlValueType.getTypeName() << "'!" );
206 }
207 #endif
208 }
209
210 return aReturn;
211 }
212
213 namespace {
214
215 // This comparison functor assumes an underlying set of constants with pairwise
216 // unequal values that are all of UNO SHORT or LONG type:
217 struct CompareConstants {
operator ()pcr::__anon8f19622e0211::CompareConstants218 bool operator ()(
219 css::uno::Reference< css::reflection::XConstantTypeDescription > const &
220 c1,
221 css::uno::Reference< css::reflection::XConstantTypeDescription > const &
222 c2) const
223 {
224 return c1->getConstantValue().get<sal_Int32>()
225 < c2->getConstantValue().get<sal_Int32>();
226 }
227 };
228
229 }
230
231 // lang::XInitialization:
initialize(const uno::Sequence<uno::Any> & aArguments)232 void SAL_CALL StringRepresentation::initialize(const uno::Sequence< uno::Any > & aArguments)
233 {
234 sal_Int32 nLength = aArguments.getLength();
235 if ( !nLength )
236 return;
237
238 const uno::Any* pIter = aArguments.getConstArray();
239 m_xTypeConverter.set(*pIter++,uno::UNO_QUERY);
240 if ( nLength != 3 )
241 return;
242
243 OUString sConstantName;
244 *pIter++ >>= sConstantName;
245 *pIter >>= m_aValues;
246
247 if ( !m_xContext.is() )
248 return;
249
250 uno::Reference< container::XHierarchicalNameAccess > xTypeDescProv(
251 m_xContext->getValueByName("/singletons/com.sun.star.reflection.theTypeDescriptionManager"),
252 uno::UNO_QUERY_THROW );
253
254 m_xTypeDescription.set( xTypeDescProv->getByHierarchicalName( sConstantName ), uno::UNO_QUERY_THROW );
255 uno::Sequence<
256 uno::Reference< reflection::XConstantTypeDescription > >
257 cs(m_xTypeDescription->getConstants());
258 std::sort(cs.begin(), cs.end(), CompareConstants());
259 m_aConstants = cs;
260 }
261
convertSimpleToString(const uno::Any & _rValue)262 OUString StringRepresentation::convertSimpleToString( const uno::Any& _rValue )
263 {
264 OUString sReturn;
265 if ( m_xTypeConverter.is() && _rValue.hasValue() )
266 {
267 try
268 {
269 if ( m_aConstants.hasElements() )
270 {
271 sal_Int16 nConstantValue = 0;
272 if ( _rValue >>= nConstantValue )
273 {
274 const uno::Reference< reflection::XConstantTypeDescription>* pIter = m_aConstants.getConstArray();
275 const uno::Reference< reflection::XConstantTypeDescription>* pEnd = pIter + m_aConstants.getLength();
276 for(sal_Int32 i = 0;pIter != pEnd;++pIter,++i)
277 {
278 if ( (*pIter)->getConstantValue() == _rValue )
279 {
280 OSL_ENSURE(i < m_aValues.getLength() ,"StringRepresentation::convertSimpleToString: Index is not in range of m_aValues");
281 sReturn = m_aValues[i];
282 break;
283 }
284 }
285 }
286 }
287
288 if ( sReturn.isEmpty() )
289 m_xTypeConverter->convertToSimpleType( _rValue, uno::TypeClass_STRING ) >>= sReturn;
290 }
291 catch( const script::CannotConvertException& ) { }
292 catch( const lang::IllegalArgumentException& ) { }
293 }
294 return sReturn;
295 }
296
297
298 namespace
299 {
300 struct ConvertIntegerFromAndToString
301 {
operator ()pcr::__anon8f19622e0311::ConvertIntegerFromAndToString302 OUString operator()( sal_Int32 _rIntValue ) const
303 {
304 return OUString::number( _rIntValue );
305 }
operator ()pcr::__anon8f19622e0311::ConvertIntegerFromAndToString306 sal_Int32 operator()( const OUString& _rStringValue ) const
307 {
308 return _rStringValue.toInt32();
309 }
310 };
311
312 struct StringIdentity
313 {
operator ()pcr::__anon8f19622e0311::StringIdentity314 OUString operator()( const OUString& _rValue ) const
315 {
316 return _rValue;
317 }
318 };
319
320 template < class ElementType, class Transformer >
composeSequenceElements(const Sequence<ElementType> & _rElements,const Transformer & _rTransformer)321 OUString composeSequenceElements( const Sequence< ElementType >& _rElements, const Transformer& _rTransformer )
322 {
323 OUStringBuffer sCompose;
324
325 // loop through the elements and concatenate the string representations of the integers
326 // (separated by a line break)
327 for (const auto& rElement : _rElements)
328 {
329 sCompose.append(OUString(_rTransformer(rElement)));
330 sCompose.append("\n");
331 }
332 sCompose.stripEnd('\n');
333
334 return sCompose.makeStringAndClear();
335 }
336
337 template < class ElementType, class Transformer >
splitComposedStringToSequence(const OUString & _rComposed,Sequence<ElementType> & _out_SplitUp,const Transformer & _rTransformer)338 void splitComposedStringToSequence( const OUString& _rComposed, Sequence< ElementType >& _out_SplitUp, const Transformer& _rTransformer )
339 {
340 _out_SplitUp.realloc( 0 );
341 if ( _rComposed.isEmpty() )
342 return;
343 sal_Int32 tokenPos = 0;
344 do
345 {
346 _out_SplitUp.realloc( _out_SplitUp.getLength() + 1 );
347 _out_SplitUp[ _out_SplitUp.getLength() - 1 ] = static_cast<ElementType>(_rTransformer( _rComposed.getToken( 0, '\n', tokenPos ) ));
348 }
349 while ( tokenPos != -1 );
350 }
351 }
352
353
convertGenericValueToString(const uno::Any & _rValue,OUString & _rStringRep)354 bool StringRepresentation::convertGenericValueToString( const uno::Any& _rValue, OUString& _rStringRep )
355 {
356 bool bCanConvert = true;
357
358 switch ( _rValue.getValueTypeClass() )
359 {
360 case uno::TypeClass_STRING:
361 _rValue >>= _rStringRep;
362 break;
363
364 case uno::TypeClass_BOOLEAN:
365 {
366 bool bValue = false;
367 _rValue >>= bValue;
368 _rStringRep = bValue ? PcrRes(RID_RSC_ENUM_YESNO[1])
369 : PcrRes(RID_RSC_ENUM_YESNO[0]);
370 }
371 break;
372
373 // some sequence types
374 case uno::TypeClass_SEQUENCE:
375 {
376 Sequence< OUString > aStringValues;
377 Sequence< sal_Int8 > aInt8Values;
378 Sequence< sal_uInt16 > aUInt16Values;
379 Sequence< sal_Int16 > aInt16Values;
380 Sequence< sal_uInt32 > aUInt32Values;
381 Sequence< sal_Int32 > aInt32Values;
382
383 // string sequences
384 if ( _rValue >>= aStringValues )
385 {
386 _rStringRep = composeSequenceElements( aStringValues, StringIdentity() );
387 }
388 // byte sequences
389 else if ( _rValue >>= aInt8Values )
390 {
391 _rStringRep = composeSequenceElements( aInt8Values, ConvertIntegerFromAndToString() );
392 }
393 // uInt16 sequences
394 else if ( _rValue >>= aUInt16Values )
395 {
396 _rStringRep = composeSequenceElements( aUInt16Values, ConvertIntegerFromAndToString() );
397 }
398 // Int16 sequences
399 else if ( _rValue >>= aInt16Values )
400 {
401 _rStringRep = composeSequenceElements( aInt16Values, ConvertIntegerFromAndToString() );
402 }
403 // uInt32 sequences
404 else if ( _rValue >>= aUInt32Values )
405 {
406 _rStringRep = composeSequenceElements( aUInt32Values, ConvertIntegerFromAndToString() );
407 }
408 // Int32 sequences
409 else if ( _rValue >>= aInt32Values )
410 {
411 _rStringRep = composeSequenceElements( aInt32Values, ConvertIntegerFromAndToString() );
412 }
413 else
414 bCanConvert = false;
415 }
416 break;
417 case uno::TypeClass_CONSTANT:
418 {
419 int i = 0;
420 ++i;
421 }
422 break;
423
424 // some structs
425 case uno::TypeClass_STRUCT:
426 OSL_FAIL( "StringRepresentation::convertGenericValueToString(STRUCT): this is dead code - isn't it?" );
427 if ( _rValue.getValueType().equals( cppu::UnoType< util::Date >::get() ))
428 {
429 // weird enough, the string representation of dates, as used
430 // by the control displaying dates, and thus as passed through the layers,
431 // is YYYYMMDD.
432 util::Date aUnoDate;
433 _rValue >>= aUnoDate;
434 _rStringRep = ::dbtools::DBTypeConversion::toDateString(aUnoDate);
435 }
436 else if ( _rValue.getValueType().equals( cppu::UnoType< util::Time >::get() ))
437 {
438 // similar for time (HHMMSSHH)
439 util::Time aUnoTime;
440 _rValue >>= aUnoTime;
441 _rStringRep = ::dbtools::DBTypeConversion::toTimeString(aUnoTime);
442 }
443 else if ( _rValue.getValueType().equals( cppu::UnoType< util::DateTime >::get() ))
444 {
445 util::DateTime aUnoDateTime;
446 _rValue >>= aUnoDateTime;
447 _rStringRep = ::dbtools::DBTypeConversion::toDateTimeString(aUnoDateTime);
448 }
449 else
450 bCanConvert = false;
451 break;
452
453 default:
454 bCanConvert = false;
455 break;
456 }
457
458 return bCanConvert;
459 }
460
convertStringToSimple(const OUString & _rValue,const uno::TypeClass & _ePropertyType)461 uno::Any StringRepresentation::convertStringToSimple( const OUString& _rValue,const uno::TypeClass& _ePropertyType )
462 {
463 uno::Any aReturn;
464 if ( m_xTypeConverter.is() && !_rValue.isEmpty() )
465 {
466 try
467 {
468 if ( m_aConstants.hasElements() && m_aValues.hasElements() )
469 {
470 const OUString* pIter = m_aValues.getConstArray();
471 const OUString* pEnd = pIter + m_aValues.getLength();
472 for(sal_Int32 i = 0;pIter != pEnd;++pIter,++i)
473 {
474 if ( *pIter == _rValue )
475 {
476 OSL_ENSURE(i < m_aConstants.getLength() ,"StringRepresentation::convertSimpleToString: Index is not in range of m_aValues");
477 aReturn = m_aConstants[i]->getConstantValue();
478 break;
479 }
480 }
481 }
482
483 if ( !aReturn.hasValue() )
484 aReturn = m_xTypeConverter->convertToSimpleType( makeAny( _rValue ), _ePropertyType );
485 }
486 catch( const script::CannotConvertException& ) { }
487 catch( const lang::IllegalArgumentException& ) { }
488 }
489 return aReturn;
490 }
491
convertStringToGenericValue(const OUString & _rStringRep,uno::Any & _rValue,const uno::Type & _rTargetType)492 bool StringRepresentation::convertStringToGenericValue( const OUString& _rStringRep, uno::Any& _rValue, const uno::Type& _rTargetType )
493 {
494 bool bCanConvert = true;
495
496 switch ( _rTargetType.getTypeClass() )
497 {
498 case uno::TypeClass_STRING:
499 _rValue <<= _rStringRep;
500 break;
501
502 case uno::TypeClass_BOOLEAN:
503 {
504 _rValue <<= PcrRes(RID_RSC_ENUM_YESNO[0]) != _rStringRep;
505 }
506 break;
507
508 case uno::TypeClass_SEQUENCE:
509 {
510 uno::Type aElementType = ::comphelper::getSequenceElementType( _rTargetType );
511
512 switch ( aElementType.getTypeClass() )
513 {
514 case uno::TypeClass_STRING:
515 {
516 Sequence< OUString > aElements;
517 splitComposedStringToSequence( _rStringRep, aElements, StringIdentity() );
518 _rValue <<= aElements;
519 }
520 break;
521 case uno::TypeClass_SHORT:
522 {
523 Sequence< sal_Int16 > aElements;
524 splitComposedStringToSequence( _rStringRep, aElements, ConvertIntegerFromAndToString() );
525 _rValue <<= aElements;
526 }
527 break;
528 case uno::TypeClass_UNSIGNED_SHORT:
529 {
530 Sequence< sal_uInt16 > aElements;
531 splitComposedStringToSequence( _rStringRep, aElements, ConvertIntegerFromAndToString() );
532 _rValue <<= aElements;
533 }
534 break;
535 case uno::TypeClass_LONG:
536 {
537 Sequence< sal_Int32 > aElements;
538 splitComposedStringToSequence( _rStringRep, aElements, ConvertIntegerFromAndToString() );
539 _rValue <<= aElements;
540 }
541 break;
542 case uno::TypeClass_UNSIGNED_LONG:
543 {
544 Sequence< sal_uInt32 > aElements;
545 splitComposedStringToSequence( _rStringRep, aElements, ConvertIntegerFromAndToString() );
546 _rValue <<= aElements;
547 }
548 break;
549 case uno::TypeClass_BYTE:
550 {
551 Sequence< sal_Int8 > aElements;
552 splitComposedStringToSequence( _rStringRep, aElements, ConvertIntegerFromAndToString() );
553 _rValue <<= aElements;
554 }
555 break;
556 default:
557 bCanConvert = false;
558 break;
559 }
560 }
561 break;
562
563 case uno::TypeClass_STRUCT:
564 OSL_FAIL( "StringRepresentation::convertStringToGenericValue(STRUCT): this is dead code - isn't it?" );
565 if ( _rTargetType.equals( cppu::UnoType< util::Date >::get() ))
566 {
567 // weird enough, the string representation of dates, as used
568 // by the control displaying dates, and thus as passed through the layers,
569 // is YYYYMMDD.
570
571 _rValue <<= ::dbtools::DBTypeConversion::toDate(_rStringRep);
572 }
573 else if ( _rTargetType.equals( cppu::UnoType< util::Time >::get() ))
574 {
575 // similar for time (HHMMSSHH)
576 _rValue <<= ::dbtools::DBTypeConversion::toTime(_rStringRep);
577 }
578 else if ( _rTargetType.equals( cppu::UnoType< util::DateTime >::get() ))
579 {
580 _rValue <<= ::dbtools::DBTypeConversion::toDateTime(_rStringRep);
581 }
582 else
583 bCanConvert = false;
584 break;
585
586 default:
587 bCanConvert = false;
588 break;
589 }
590
591 return bCanConvert;
592 }
593
594
595 } // pcr
596
597
598 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
extensions_propctrlr_StringRepresentation_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)599 extensions_propctrlr_StringRepresentation_get_implementation(
600 css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
601 {
602 return cppu::acquire(new pcr::StringRepresentation(context));
603 }
604
605
606 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
607