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