// Determine the format that will be used when interpreting a value to write, // or when presenting a value for which default formatting has been requested. void QEStringFormatting::determineDbFormat( const QVariant &value ) { // Assume default formatting, and only a single value dbFormat = FORMAT_DEFAULT; dbFormatArray = false; // Get the value type QVariant::Type t = value.type(); // If the value is a list, get the type of the first element in the list if( t == QVariant::List ) { // Note that whatever the format, we have an array of them dbFormatArray = true; // Get the list const QVariantList valueArray = value.toList(); // If the list has anything in it, get the type of the first if( valueArray.count() ) { t = valueArray[0].type(); } else { formatFailure( QString( "Bug in QEStringFormatting::determineDefaultFormatting(). Empty array" ) ); return; } } // Determine the formatting type from the variant type switch( t ) { case QVariant::Double: dbFormat = FORMAT_FLOATING; break; case QVariant::LongLong: case QVariant::Int: dbFormat = FORMAT_INTEGER; break; case QVariant::ULongLong: case QVariant::UInt: dbFormat = FORMAT_UNSIGNEDINTEGER; break; case QVariant::String: dbFormat = FORMAT_STRING; break; default: formatFailure( QString( "Bug in QEStringFormatting::determineDbFormatting(). The QVariant type was not expected" ) ); break; } }
/*! Generate an integer given a value, using formatting defined within this class. */ long QCaIntegerFormatting::formatInteger( const QVariant &value ) { // Determine the format from the variant type. // Only the types used to store ca data are used. any other type is considered a failure. switch( value.type() ) { case QVariant::Double : { return formatFromFloating( value ); } case QVariant::LongLong : { return value.toLongLong(); // No conversion requried. Stored in variant as required type } case QVariant::ULongLong : { return formatFromUnsignedInteger( value ); } case QVariant::String : { return formatFromString( value ); } default : { return formatFailure( QString( "Bug in QCaIntegerFormatting::formatInteger(). The QVariant type was not expected" ) ); } } }
/* Format a variant value as an integer representation of a string. This method was written to convert a QVariant of type String, but should cope with a variant of any type. Convert the variant value to an unsigned long. It may or may not be a ulonglong type variant. If it is - good, there will be no conversion problems. */ long QEIntegerFormatting::formatFromString( const QVariant &value ) { // Extract the value as a long using whatever conversion the QVariant uses. // If that fails, try extracting the value as a double using whatever conversion the QVariant uses, then cast it as a long. // // Note, this will not pick up if the QVariant type is not one of the types used to represent CA data. // This is OK as it is not absolutely nessesary to do this sort of check at this point. Also the code is more robust as it will // work if the range of QVariant types used expands. // Note, this does not give us the freedom to specify what conversions should fail or succeed. For example, does QVariant::toLongLong() // work if the value it holds is the string 1.0001 and should it? // If QVariant::toLongLong() does not do exactly what is required, a switch statement for each of the types used to hold CA data // will need to be added and the conversions done manually or using QVariant::toLongLong() as required. bool convertOk; long lValue = value.toLongLong( &convertOk ); if( convertOk ) return lValue; double dValue = value.toDouble( &convertOk ); if( convertOk ) { lValue = (long)dValue; return lValue; } return formatFailure( QString( "Warning from QEIntegerFormatting::formatFromString(). A variant could not be converted to a string." ) ); }
/* Format a variant value as a string representation of an unsigned integer. This method was written to convert a QVariant of type ULongLong, but should cope with a variant of any type. First convert the variant value to an unsigned long. It may or may not be a ulonglong type variant. If it is - good, there will be no conversion problems. Then format it as a string using the formatting information stored in this class. */ void QEStringFormatting::formatFromUnsignedInteger( const QVariant &value ) { // Extract the value as an unsigned long using whatever conversion the QVariant uses. // // Note, this will not pick up if the QVariant type is not one of the types used to represent CA data. // This is OK as it is not absolutely nessesary to do this sort of check at this point. Also the code is more robust as it will // work if the range of QVariant types used expands. // Note, this does not give us the freedom to specify what conversions should fail or succeed. For example, does QVariant::toULongLong() // work if the value it holds is the string 1.000 and should it? // If QVariant::toULongLong() does not do exactly what is required, a switch statement for each of the types used to hold CA data // will need to be added and the conversions done manually or using QVariant::toULongLong() as required. // Use QString conversions is variant is a string. // (QVariant toLongLong can't convert strings like "2.000"!) bool convertOk; unsigned long ulValue; if( value.type() == QVariant::String ) { QString str = value.toString(); double dd = str.toDouble( &convertOk ); ulValue = dd; } // Use QVariant conversions otherwise else { ulValue = value.toULongLong( &convertOk ); } if( !convertOk ) { formatFailure( QString( "Warning from QEStringFormatting::formatFromUnsignedInteger(). A variant could not be converted to an unsigned long." ) ); return; } // Generate the text stream << ulValue; // Add sperators if needs be outStr = insertSeparators( outStr ); }
/* Format a variant value as a string representation of a floating point number. First convert the variant value to a double. It may or may not be a floating point type variant. If it is - good, there will be no conversion problems. Then format it as a string using the formatting information stored in this class. */ void QEStringFormatting::formatFromFloating( const QVariant &value ) { // Extract the value as a double using whatever conversion the QVariant uses. // // Note, this will not pick up if the QVariant type is not one of the types used to represent CA data. // This is OK as it is not absolutely nessesary to do this sort of check at this point. Also the code is more robust as it will // work if the range of QVariant types used expands. // Note, this does not give us the freedom to specify what conversions should fail or succeed. For example, does QVariant::toDouble() // work if the value it holds is the string 1.234 10^6, or does it work for both - 1.234 and -1.234, and should it? // If QVariant::toDouble() does not do exactly what is required, a switch statement for each of the types used to hold CA data // will need to be added and the conversions done manually or using QVariant::toDouble() as required. bool convertOk; double dValue = value.toDouble( &convertOk ); if( !convertOk ) { formatFailure( QString( "Warning from QEStringFormatting::formatFromFloating(). A variant could not be converted to a long." ) ); return; } // NOTE: Smart notation (NOTATION_AUTOMATIC) does not honor real number precision. // So select FixedNotation or ScientificNotation as appropriate. // QTextStream::RealNumberNotation rnn = stream.realNumberNotation(); if( rnn == QTextStream::SmartNotation ){ int prec; double low_fixed_limit; double high_fixed_limit; double absDbValue; // Extact precision being used. prec = stream.realNumberPrecision (); prec = LIMIT( prec, 0, 15 ); // Example, if prec = 3, when low limit is 0.01 low_fixed_limit = EXP10( 1 - prec ); high_fixed_limit = 1.0E+05; // Work with absoloute value absDbValue = ABS( dValue ); if( absDbValue == 0.0 || ( absDbValue >= low_fixed_limit && absDbValue < high_fixed_limit )){ stream.setRealNumberNotation( QTextStream::FixedNotation ); } else { stream.setRealNumberNotation( QTextStream::ScientificNotation ); } } // Generate the text stream << dValue; stream.setRealNumberNotation( rnn ); // reset // Remove leading zero if required if( !leadingZero ) { if( outStr.left(2) == "0." ) outStr = outStr.right( outStr.length()-1); } // Remove trailing zeros if required, but don't leave a naked decimal point, i.e. 4.0000 becomes 4.0 (as opposed to 4.) // Also need to be careful to ensure 1.23000e+100 becomes 1.23e+100 (as opposed to 1.23000e+1) if( !trailingZeros ) { int dp = outStr.indexOf( ".", 0 ); if( dp >= 0 ){ int ep = outStr.indexOf( "e", dp ); // find exponent if it exists if( ep < 0 ) ep = outStr.indexOf( "E", dp ); // allow for either case if( ep < 0 ) ep = outStr.length (); // otherwise start at end of string int sp = ep; while( ( sp > dp + 2 ) && ( outStr[sp - 1] == '0' ) ){ sp--; } if( sp < ep ){ outStr.replace( sp, ep - sp, "" ); } } } // Add sperators if needs be outStr = insertSeparators( outStr ); }
/* Generate a string given an element value, using formatting defined within this class. */ QString QEStringFormatting::formatElementString( const QVariant& value ) { // Examine the value and note the matching format determineDbFormat( value ); // Initialise bool errorMessage = false; // Set if an error message is the result outStr.clear(); // Set the precision if( useDbPrecision ) stream.setRealNumberPrecision( dbPrecision ); else stream.setRealNumberPrecision( precision ); // Format the value as requested switch( format ) { // Determine the format from the variant type. // Only the types used to store ca data are used. any other type is // considered a failure. case FORMAT_DEFAULT : { bool haveEnumeratedString = false; // Successfully converted the value to an enumerated string // If a list of enumerated strings is available, attempt to get an enumerated string if( dbEnumerations.size() ) { // Ensure the input value can be used as an index into the list of enumerated strings bool convertOk; long lValue = value.toLongLong( &convertOk ); if( convertOk && lValue >= 0 ) { // Get the appropriate enumerated string if( lValue < dbEnumerations.size() ) { outStr = dbEnumerations[lValue]; haveEnumeratedString = true; } // NOTE: STAT field hard-coded values now set up in QCaObject.cpp - extra values appended to dbEnumerations. } } // If no list of enumerated strings was available, or a string could not be selected, // convert the value based on it's type. if( !haveEnumeratedString ) { switch( dbFormat ) { case FORMAT_FLOATING: formatFromFloating( value ); break; case FORMAT_INTEGER: formatFromInteger( value ); break; case FORMAT_UNSIGNEDINTEGER: formatFromUnsignedInteger( value ); break; case FORMAT_STRING: formatFromString( value ); break; default: formatFailure( QString( "Bug in QEStringFormatting::formatString(). The QVariant type was not expected" ) ); errorMessage = true; break; } } break; } // Format as requested, ignoring the database type case FORMAT_FLOATING: formatFromFloating( value ); break; case FORMAT_INTEGER: formatFromInteger( value ); break; case FORMAT_UNSIGNEDINTEGER: formatFromUnsignedInteger( value ); break; case FORMAT_LOCAL_ENUMERATE: formatFromEnumeration( value ); break; case FORMAT_TIME: formatFromTime( value ); break; case FORMAT_STRING: formatFromString( value ); break; // Don't know how to format. // This is a code error. All cases in QEStringFormatting::formats should be catered for default: formatFailure( QString( "Bug in QEStringFormatting::format(). The format type was not expected" ) ); errorMessage = true; break; } return outStr; }