/************************************************************************* * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: valueconverter.cxx,v $ * * $Revision: 1.22 $ * * last change: $Author: obo $ $Date: 2006-09-16 15:35:47 $ * * The Contents of this file are made available subject to * the terms of GNU Lesser General Public License Version 2.1. * * * GNU Lesser General Public License Version 2.1 * ============================================= * Copyright 2005 by Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, CA 94303, USA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * This library 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 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_configmgr.hxx" #include "valuetypeconverter.hxx" #ifndef CONFIGMGR_TYPECONVERTER_HXX #include "typeconverter.hxx" #endif inline sal_Bool rtl_ascii_isWhitespace( sal_Unicode ch ) { return ch <= 0x20 && ch; } namespace configmgr { using rtl::OUString; using namespace com::sun::star::uno; using namespace std; // ----------------------------------------------------------------------------- typedef ValueConverter::StringList StringList; // ----------------------------------------------------------------------------- static void throwConversionError(sal_Char const* pErrorMsg) CFG_THROW1( script::CannotConvertException ) { OSL_ENSURE(false, pErrorMsg); script::CannotConvertException error; error.Message = OUString::createFromAscii(pErrorMsg); throw error; } // ----------------------------------------------------------------------------- template inline bool charInRange(Char ch, char from, char to) throw() { return Char(from) <= ch && ch <= Char(to); } // ----------------------------------------------------------------------------- static inline unsigned makeHexNibble(unsigned char ch) CFG_THROW1 ( script::CannotConvertException) { unsigned nRet = 0; if (charInRange(ch, '0', '9')) nRet = ch - unsigned('0'); else if (charInRange(ch, 'a', 'f')) nRet = ch - unsigned('a' - 10u); else if (charInRange(ch, 'A', 'F')) nRet = ch - unsigned('A' - 10u); else throwConversionError("Invalid Hex Character in binary value"); return nRet; } // ----------------------------------------------------------------------------- static inline unsigned readHexNibble(sal_Unicode ch) CFG_THROW1 ( script::CannotConvertException) { if (!charInRange(ch, 0, 127)) throwConversionError("Non-Ascii Character in binary value"); return makeHexNibble(static_cast(ch)); } // ----------------------------------------------------------------------------- static inline unsigned int readHexByte(sal_Unicode const*& pStr) CFG_THROW1 ( script::CannotConvertException) { register unsigned int nHigh = readHexNibble(*pStr++); register unsigned int nLow = readHexNibble(*pStr++); return (nHigh << 4) | nLow; } // ----------------------------------------------------------------------------- static void parseHexBinary(OUString const& aHexString_, uno::Sequence& rBinarySeq_) CFG_UNO_THROW1 ( script::CannotConvertException ) { // PRE: aBinaryString with HexCode // POST: rBinarySeq with the to Hex converted String sal_uInt32 nCount = aHexString_.getLength(); sal_Unicode const * pHex = aHexString_.getStr(); if (nCount % 2) throwConversionError("Hex string has odd number of characters"); nCount /= 2; rBinarySeq_.realloc(nCount); sal_Int8 * pBinary = rBinarySeq_.getArray(); while (nCount--) { *pBinary++ = static_cast(readHexByte(pHex)); } } // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- uno::Sequence ValueConverter::parseBinary(OUString const& aBinaryString_) const CFG_UNO_THROW1 ( script::CannotConvertException) { uno::Sequence aResultSeq; parseHexBinary(aBinaryString_,aResultSeq); return aResultSeq; } // ----------------------------------------------------------------------------- static inline uno::Type getBinaryType() { uno::Sequence const * const for_binary = 0; return ::getCppuType(for_binary); } // ----------------------------------------------------------------------------- bool ValueConverter::isList() const { return m_aType.getTypeClass() == uno::TypeClass_SEQUENCE && m_aType != getBinaryType(); } // ----------------------------------------------------------------------------- uno::Any ValueConverter::convertToAny(OUString const& aContent) const CFG_UNO_THROW1( script::CannotConvertException) { uno::Any aValue; if (this->isNull()) { OSL_ENSURE(aContent.trim().getLength() == 0, "ValueConverter: Non-empty Null Value - ignoring content"); OSL_ASSERT(!aValue.hasValue()); } else if (this->isList()) { StringList aContentList; splitListData(aContent, aContentList); convertListToAny(aContentList, aValue); } else { convertScalarToAny(aContent, aValue); } return aValue; } // ----------------------------------------------------------------------------- bool ValueConverter::convertScalarToAny(OUString const& aContent, uno::Any& rValue) const CFG_UNO_THROW1 ( script::CannotConvertException ) { OSL_PRECOND(!this->isNull(),"ValueConverter::convertScalarToAny - check for NULL before calling"); OSL_ENSURE(m_aType.getTypeClass() != uno::TypeClass_ANY,"'Any' values must be NULL"); // check for Binary if (m_aType == getBinaryType()) { Sequence aBinarySeq = parseBinary(aContent); rValue <<= aBinarySeq; } else { rValue = toAny(m_xTypeConverter, aContent, m_aType.getTypeClass()); } return !! rValue.hasValue(); } // ----------------------------------------------------------------------------- template bool convertListToSequence(StringList const& aStringList, uno::Sequence< T >& rSequence, uno::TypeClass aElementTypeClass, ValueConverter const& rConverter) CFG_UNO_THROW1 ( script::CannotConvertException ) { OSL_ASSERT(aElementTypeClass == ::getCppuType(static_cast(0)).getTypeClass()); rSequence.realloc(aStringList.size()); sal_uInt32 nPos = 0; for(StringList::const_iterator it = aStringList.begin(); it != aStringList.end(); ++it) { uno::Any aValueAny = toAny(rConverter.getTypeConverter(), *it, aElementTypeClass); if (aValueAny >>= rSequence[nPos]) ++nPos; else if (!aValueAny.hasValue()) OSL_ENSURE(false,"UNEXPECTED: Found NULL value in List - ignoring value !"); else OSL_ENSURE(false,"ERROR: Cannot extract converted value into List - skipping value !"); } bool bOK = (nPos == aStringList.size()); if (!bOK) { OSL_ASSERT(nPos < aStringList.size()); rSequence.realloc(nPos); } return bOK; } // ----------------------------------------------------------------------------- // special conversion for string sequence static inline void stringListToSequence(uno::Sequence< OUString > & rSequence, StringList const & aStringList) { rSequence .realloc( aStringList.size() ); std::copy( aStringList.begin(), aStringList.end(), rSequence.getArray() ); } // ----------------------------------------------------------------------------- static inline StringList sequenceToStringList(uno::Sequence< OUString > const & aSequence) { OUString const * const pBegin = aSequence.getConstArray(); OUString const * const pEnd = pBegin + aSequence.getLength(); return StringList(pBegin,pEnd); } // ----------------------------------------------------------------------------- uno::Sequence< OUString > ValueConverter::splitStringList(OUString const& aContent) const { StringList aList; splitListData(aContent, aList); uno::Sequence< OUString > aResult; stringListToSequence(aResult,aList); return aResult; } // ----------------------------------------------------------------------------- uno::Any ValueConverter::convertListToAny(uno::Sequence< OUString > const& aContentList) const CFG_UNO_THROW1( script::CannotConvertException ) { uno::Any aResult; StringList const aStringList = sequenceToStringList(aContentList); convertListToAny(aStringList,aResult); return aResult; } // ----------------------------------------------------------------------------- // special overload for binary sequence // template<> // use an explicit specialization bool convertListToSequence(StringList const& aStringList, uno::Sequence< uno::Sequence >& rSequence, uno::TypeClass aElementTypeClass, ValueConverter const& rParser ) CFG_UNO_THROW1 ( script::CannotConvertException ) { { (void)aElementTypeClass; } OSL_ASSERT(aElementTypeClass == uno::TypeClass_SEQUENCE); rSequence.realloc(aStringList.size()); sal_uInt32 nPos = 0; for(StringList::const_iterator it = aStringList.begin(); it != aStringList.end(); ++it) { rSequence[nPos++] = rParser.parseBinary(*it); } return true; } // ----------------------------------------------------------------------------- // special overload for string sequence // template<> // use an explicit specialization bool convertListToSequence(StringList const& aStringList, uno::Sequence< OUString >& rSequence, uno::TypeClass aElementTypeClass, ValueConverter const& /*rParser*/ ) CFG_UNO_THROW1 ( script::CannotConvertException ) { { (void)aElementTypeClass; } OSL_ASSERT(aElementTypeClass == uno::TypeClass_STRING); stringListToSequence(rSequence, aStringList); return true; } // ----------------------------------------------------------------------------- #define MAYBE_EXTRACT_SEQUENCE( type ) \ if (aElementType == ::getCppuType( (type const *)0)) \ { \ Sequence< type > aSequence; \ convertListToSequence(aContentList,aSequence,aElementTypeClass, *this); \ rValue <<= aSequence; \ } bool ValueConverter::convertListToAny(StringList const& aContentList, uno::Any& rValue) const CFG_UNO_THROW1 ( script::CannotConvertException ) { OSL_PRECOND(!this->isNull(),"ValueConverter::convertListToAny - check for NULL before calling"); OSL_ENSURE(m_aType.getTypeClass() == uno::TypeClass_SEQUENCE,"'Any' not allowed for lists"); uno::Type aElementType = getSequenceElementType(m_aType); uno::TypeClass aElementTypeClass = aElementType.getTypeClass(); OSL_ENSURE(aElementTypeClass != uno::TypeClass_ANY,"'Any' not allowed for list elements"); MAYBE_EXTRACT_SEQUENCE( OUString ) else MAYBE_EXTRACT_SEQUENCE( sal_Bool ) else MAYBE_EXTRACT_SEQUENCE( sal_Int16 ) else MAYBE_EXTRACT_SEQUENCE( sal_Int32 ) else MAYBE_EXTRACT_SEQUENCE( sal_Int64 ) else MAYBE_EXTRACT_SEQUENCE( double ) else MAYBE_EXTRACT_SEQUENCE( Sequence ) else { OSL_ENSURE(false, "Unknown element type in list"); throwConversionError("Invalid value-type found in list value"); } return !! rValue.hasValue(); } #undef MAYBE_EXTRACT_SEQUENCE // ----------------------------------------------------------------------------- namespace { sal_Int32 const NO_MORE_TOKENS = -1; struct OTokenizeByWhitespace { static inline bool isWhitespace(sal_Unicode ch) { // note: for definition of whitescape see also // canUseWhitespace(OUString const&) // in xmlformater.cxx // ----------------------------------------------------------------------------- return rtl_ascii_isWhitespace(ch) ? true : false; } sal_Int32 findFirstTokenStart(OUString const& sText) const CFG_NOTHROW() { return findNextTokenStart(sText,0); } sal_Int32 findNextTokenStart(OUString const& sText, sal_Int32 nPrevTokenEnd) const CFG_NOTHROW() { sal_Int32 const nEnd = sText.getLength(); sal_Int32 nPos = nPrevTokenEnd; OSL_PRECOND( nPos == 0 || (0 < nPos && nPos < nEnd && isWhitespace(sText[nPos])) || nPos == nEnd, "Invalid nPrevTokenEnd"); while (nPos < nEnd && isWhitespace(sText[nPos])) { ++nPos; } if (nPos < nEnd) return nPos; else return NO_MORE_TOKENS; } sal_Int32 findTokenEnd(OUString const& sText, sal_Int32 nTokenStart) const CFG_NOTHROW() { sal_Int32 const nEnd = sText.getLength(); sal_Int32 nPos = nTokenStart; OSL_PRECOND( 0 <= nPos && nPos < nEnd && !isWhitespace(sText[nPos]), "Invalid nTokenStart"); while (nPos < nEnd && !isWhitespace(sText[nPos])) { ++nPos; } return nPos; } }; // ----------------------------------------------------------------------------- struct OTokenizeBySeparator { OUString const sSeparator; OTokenizeBySeparator(OUString const& _sSeparator) CFG_NOTHROW() : sSeparator(_sSeparator) { OSL_PRECOND(sSeparator.trim().getLength() > 0, "Invalid empty separator string"); } sal_Int32 findFirstTokenStart(OUString const& /*sText*/) const CFG_NOTHROW() { return 0; } sal_Int32 findNextTokenStart(OUString const& sText, sal_Int32 nPrevTokenEnd) const CFG_NOTHROW() { sal_Int32 const nEnd = sText.getLength(); sal_Int32 nPos = nPrevTokenEnd; OSL_PRECOND( nPos == nEnd || (0 <= nPos && nPos < nEnd && sText.indexOf(sSeparator, nPos) == nPos), "Invalid nPrevTokenEnd"); if (nPos < nEnd) return nPos + sSeparator.getLength(); else return NO_MORE_TOKENS; } sal_Int32 findTokenEnd(OUString const& sText, sal_Int32 nTokenStart) const CFG_NOTHROW() { sal_Int32 const nEnd = sText.getLength(); OSL_PRECOND( 0 <= nTokenStart && nTokenStart <= nEnd , "Invalid nTokenStart"); sal_Int32 nPos = sText.indexOf(sSeparator,nTokenStart); if (nPos >= 0) return nPos; else return nEnd; } }; // ----------------------------------------------------------------------------- template void tokenizeListData(Tokenizer const& aTokenizer, OUString const& aContent, StringList& rContentList) CFG_NOTHROW( ) { sal_Int32 nTokenPos = aTokenizer.findFirstTokenStart(aContent); while(nTokenPos != NO_MORE_TOKENS) { sal_Int32 nTokenEnd = aTokenizer.findTokenEnd(aContent, nTokenPos); // this is what the tokenizer must provide OSL_ASSERT(0 <= nTokenPos && nTokenPos <= nTokenEnd && nTokenEnd <= aContent.getLength()); rContentList.push_back( aContent.copy(nTokenPos, nTokenEnd-nTokenPos) ); nTokenPos= aTokenizer.findNextTokenStart(aContent, nTokenEnd); } } // ----------------------------------------------------------------------------- } // ----------------------------------------------------------------------------- void ValueConverter::splitListData(OUString const& aContent, StringList& rContentList) const CFG_NOTHROW( ) { OUString sSeparator = m_sSeparator; bool bSeparateByWhitespace = (sSeparator.trim().getLength() == 0); if (bSeparateByWhitespace) { OSL_ENSURE( sSeparator.getLength()==0 || sSeparator.equalsAscii(" "), "Unexpected whitespace-only separator"); tokenizeListData( OTokenizeByWhitespace(), aContent, rContentList ); } else { OSL_ENSURE( sSeparator.trim()==sSeparator, "Unexpected whitespace in separator"); tokenizeListData( OTokenizeBySeparator(sSeparator), aContent, rContentList ); } } // ----------------------------------------------------------------------------- } // namespace