00001 /* 00002 * The information in this file is 00003 * Copyright(c) 2007 Ball Aerospace & Technologies Corporation 00004 * and is subject to the terms and conditions of the 00005 * GNU Lesser General Public License Version 2.1 00006 * The license text is available from 00007 * http://www.gnu.org/licenses/lgpl.html 00008 */ 00009 00010 #ifndef NITFUTILITIES_H 00011 #define NITFUTILITIES_H 00012 00013 class Classification; 00014 class DateTime; 00015 #include "DataVariant.h" 00016 #include "DynamicObject.h" 00017 #include "Endian.h" 00018 #include "NitfConstants.h" 00019 #include "NitfTreParser.h" 00020 #include "TypesFile.h" 00021 #include "StringUtilities.h" 00022 00023 #include <iomanip> 00024 #include <iostream> 00025 #include <limits> 00026 #include <set> 00027 #include <string> 00028 #include <vector> 00029 00030 #include <boost/lexical_cast.hpp> 00031 00032 namespace Nitf 00033 { 00034 /** 00035 * Returns the string representation of the given RPC coefficient. 00036 * 00037 * @param prefix 00038 * The prefix for the coefficient name. 00039 * @param num 00040 * The coefficient number to get [1, 20]. 00041 * @return string representation of the coefficient. 00042 */ 00043 std::string getRpcCoefficient(const std::string& prefix, const unsigned int& num); 00044 00045 /** 00046 * Determine the least trusted state of the provided TreStates. 00047 * 00048 * @param stateA 00049 * The first state to compare 00050 * @param stateB 00051 * The second state to compare 00052 * 00053 * @return The least trusted of the provided states. 00054 */ 00055 TreState MaxState(TreState stateA, TreState stateB); 00056 00057 /** 00058 * Represents getting the first instance of a tag. 00059 */ 00060 struct FindFirst 00061 { 00062 bool operator()(const DynamicObject& instance) 00063 { 00064 return true; 00065 } 00066 }; 00067 00068 /** 00069 * Gets a handle to a TRE out of the NITF Metadata. 00070 * 00071 * You can provide a functor to filter out which TRE to fetch (ie. find the first 00072 * ICHIPB with an OP_COL_11 field that is valid). Example: 00073 * @code 00074 * // Get the first instance of the RPC00B TRE; assumes nitfMetadata is the NITF metadata DynamicObject 00075 * DynamicObject* pDynObj = getTagHandle(nitfMetadata, "RPC00B", FindFirst()); 00076 * @endcode 00077 * 00078 * @param nitfMetadata 00079 * The cube's NITF metadata object. 00080 * @param treName 00081 * The name of the TRE. 00082 * @param cond 00083 * A functor that determines if the DynamicObject passed in should be fetched. 00084 * argument is provided, gets the first instance of the tag. 00085 * @return A pointer to the tag handle of the given TRE and instance. NULL, if the 00086 * TRE does not exist. 00087 */ 00088 template<class Condition> 00089 const DynamicObject* getTagHandle(const DynamicObject& nitfMetadata, 00090 std::string treName, Condition cond = Condition()) 00091 { 00092 std::string actualType; 00093 00094 const DynamicObject* pTre = nitfMetadata.getAttribute(Nitf::TRE_METADATA).getPointerToValue<DynamicObject>(); 00095 if (pTre == NULL) 00096 { 00097 return NULL; 00098 } 00099 00100 // now get the TRE DynamicObject 00101 pTre = pTre->getAttribute(treName).getPointerToValue<DynamicObject>(); 00102 if (pTre == NULL) 00103 { 00104 return NULL; 00105 } 00106 00107 const DynamicObject* pFound = NULL; 00108 std::vector<std::string> instances; 00109 pTre->getAttributeNames(instances); 00110 for (std::vector<std::string>::const_iterator it = instances.begin(); it != instances.end(); ++it) 00111 { 00112 const DynamicObject* pObj = pTre->getAttribute(*it).getPointerToValue<DynamicObject>(); 00113 if (pObj != NULL && cond(*pObj)) 00114 { 00115 pFound = pObj; 00116 break; 00117 } 00118 } 00119 return pFound; 00120 } 00121 00122 /** 00123 * Gets the sentinel value for a given field. 00124 * @return The sentinel value. 00125 */ 00126 template<typename T> 00127 inline T getSentinel() 00128 { 00129 return std::numeric_limits<T>::max(); 00130 } 00131 00132 /** 00133 * Test a tag against a range 00134 * 00135 * @param reporter 00136 * ostream to report failures to 00137 * @param testValue 00138 * The value to test 00139 * @param minValue 00140 * The minimum value of the range 00141 * @param maxValue 00142 * The maximum value of the range 00143 * 00144 * @return SUSPECT if outside the range, VALID otherwise. 00145 */ 00146 template<typename T> 00147 TreState testTagValueRange(std::ostream& reporter, const T& testValue, const T& minValue, const T& maxValue) 00148 { 00149 if (testValue < minValue || testValue > maxValue) 00150 { 00151 reporter << "SUSPECT field: " << testValue << " should have been between " 00152 << minValue << " and " << maxValue << ", "; 00153 return SUSPECT; 00154 } 00155 00156 return VALID; 00157 } 00158 00159 /** 00160 * Test a tag within a DynamicObject against a range. 00161 * 00162 * @param tre 00163 * The DynamicObject from which to get the value. 00164 * @param reporter 00165 * ostream To report failures. 00166 * @param pNumFields 00167 * Number of fields currently read in by the calling parser. 00168 * If not \c NULL, this value is incremented if the field has been read. 00169 * @param name 00170 * The name of the tag within the DynamicObject. 00171 * @param minValue 00172 * The minimum value of the range. 00173 * @param maxValue 00174 * The maximum value of the range. 00175 * @param optional 00176 * \c True if the field is considered to be optional (all spaces allowed). 00177 * @return INVALID if the tag is not found, SUSPECT if outside the range, VALID otherwise. 00178 */ 00179 template<typename T> 00180 TreState testTagValueRange(const DynamicObject& tre, std::ostream& reporter, unsigned int* pNumFields, 00181 const std::string& name, const T& minValue, const T& maxValue, bool optional = false) 00182 { 00183 try 00184 { 00185 const T& testValue = dv_cast<T>(tre.getAttribute(name)); 00186 TreState state = VALID; 00187 if (optional == false || getSentinel<T>() != testValue) 00188 { 00189 state = testTagValueRange(reporter, testValue, minValue, maxValue); 00190 } 00191 00192 if (state != VALID) 00193 { 00194 reporter << ": field = " << name << ".\n"; 00195 } 00196 00197 if (pNumFields != NULL) 00198 { 00199 (*pNumFields)++; 00200 } 00201 00202 return state; 00203 } 00204 catch (const std::bad_cast&) 00205 { 00206 reporter << "Field " << name << " not found in Dynamic Object from testTagValueRange().\n"; 00207 } 00208 return INVALID; 00209 } 00210 00211 /** 00212 * Test a tag within a DynamicObject for equivalence 00213 * 00214 * @param tre 00215 * The DynamicObject to get the value from 00216 * @param reporter 00217 * ostream to report failures to 00218 * @param name 00219 * The name of the tag within the DynamicObject 00220 * @param eqValue 00221 * The value to test equivalence against 00222 * 00223 * @return INVALID if the tag is not found, SUSPECT if not equal, VALID otherwise. 00224 */ 00225 template<typename T> 00226 TreState testTagValueEq(const DynamicObject& tre, std::ostream& reporter, const std::string& name, const T& eqValue) 00227 { 00228 try 00229 { 00230 const T& testValue = dv_cast<T>(tre.getAttribute(name)); 00231 if (testValue == eqValue) 00232 { 00233 return VALID; 00234 } 00235 reporter << "Field " << name << " = " << testValue << " should have been equal to " << eqValue << ".\n"; 00236 return SUSPECT; 00237 } 00238 catch (std::bad_cast e) 00239 { 00240 reporter << "Field " << name << " not found in Dynamic Object from testTagValueEq().\n"; 00241 return INVALID; 00242 } 00243 00244 return UNTESTED; 00245 } 00246 00247 /** 00248 * Test a tag within a DynamicObject for non-equivalence 00249 * 00250 * @param tre 00251 * The DynamicObject to get the value from 00252 * @param reporter 00253 * ostream to report failures to 00254 * @param name 00255 * The name of the tag within the DynamicObject 00256 * @param notEqValue 00257 * The value to test non-equivalence against 00258 * 00259 * @return INVALID if the tag is not found, SUSPECT if equal, VALID otherwise. 00260 */ 00261 template<typename T> 00262 inline TreState testTagValueNotEq(const DynamicObject& tre, std::ostream& reporter, 00263 const std::string& name, const T& notEqValue) 00264 { 00265 try 00266 { 00267 const T& testValue = dv_cast<T>(tre.getAttribute(name)); 00268 if (testValue != notEqValue) 00269 { 00270 return VALID; 00271 } 00272 reporter << "Field " << name << " = " << testValue << 00273 " should have been not equal to " << notEqValue << ".\n"; 00274 return SUSPECT; 00275 } 00276 catch (std::bad_cast e) 00277 { 00278 reporter << "Field " << name << " not found in Dynamic Object from testTagValueEq().\n"; 00279 return INVALID; 00280 } 00281 00282 return UNTESTED; 00283 } 00284 00285 /** 00286 * Combination typecast and endian swap. 00287 * 00288 * @param pBuffer 00289 * Buffer containing non-swapped data 00290 * @param sourceEndian 00291 * The endianness of the data in the buffer 00292 * 00293 * @return The contents of the buffer, appropriately typed and swapped. 00294 */ 00295 template<typename T> 00296 T convertBinary(const void* pBuffer, EndianType sourceEndian) 00297 { 00298 T data; 00299 Endian endian(sourceEndian); 00300 memcpy(&data, pBuffer, sizeof(T)); 00301 endian.swapValue(data); 00302 return data; 00303 } 00304 00305 /** 00306 * Test the specified bit. 00307 * 00308 * @param bitMask 00309 * Mask to tests 00310 * @param bit 00311 * Bit of the mask to test 00312 * 00313 * @return True if the given bit is true, false otherwise. 00314 */ 00315 bool bitTest(unsigned int bitMask, unsigned int bit); 00316 00317 /** 00318 * Parse a date-time string of the form HHMM (hour minute). 00319 * 00320 * @param fDTG 00321 * The string to parse 00322 * @param hour 00323 * Upon successful return, this contains the hours from the string. 00324 * @param min 00325 * Upon successful return, this contains the minutes from the string. 00326 * 00327 * @return True if the parse succeeded, false otherwise. 00328 */ 00329 bool DtgParseHHMM(const std::string& fDTG, unsigned short& hour, unsigned short& min); 00330 00331 /** 00332 * Parse a date-time string of the form CCYYMMDDhhmm. 00333 * 00334 * @param fDTG 00335 * The string to parse 00336 * @param year 00337 * Upon successful return, this contains the year (including century) from the string. 00338 * @param month 00339 * Upon successful return, this contains the month from the string. 00340 * @param day 00341 * Upon successful return, this contains the day from the string. 00342 * @param hour 00343 * Upon successful return, this contains the hours from the string. 00344 * @param min 00345 * Upon successful return, this contains the minutes from the string. 00346 * 00347 * @return True if the parse succeeded, false otherwise. 00348 */ 00349 bool DtgParseCCYYMMDDhhmm(const std::string& fDTG, unsigned short& year, unsigned short& month, 00350 unsigned short& day, unsigned short& hour, unsigned short& min); 00351 00352 /** 00353 * Parse a date-time string of the form CCYYMMDDhhmm. 00354 * 00355 * @param fDTG 00356 * The string to parse 00357 * @param pDateTime 00358 * Upon successful return, this contains the date and time from the string. 00359 * 00360 * @return True if the parse succeeded, false otherwise. 00361 */ 00362 bool DtgParseCCYYMMDDhhmm(const std::string& fDTG, DateTime* pDateTime); 00363 00364 /** 00365 * Parse a date-time from two strings of the form CCYYMMDD and ssss.sss. 00366 * 00367 * @param date 00368 * The date string to parse 00369 * @param time 00370 * The time string to parse, this may have a variable number of digits but must be 00371 * seconds of the day 00372 * @param pDateTime 00373 * Upon successful return, this contains the date and time from the string. 00374 * @param secondsInDay 00375 * Return the seconds in the day as a float. \em pDateTime only contains 1 second resolution 00376 * so this is required to obtain fractional seconds. This is the value in time as a \c float. 00377 * 00378 * @return \c True if the parse succeeded, \c false otherwise. 00379 */ 00380 bool DtgParseCCYYMMDDssss(const std::string& date, const std::string& time, 00381 DateTime* pDateTime, float& secondsInDay); 00382 00383 /** 00384 * Parse a date-time string of the form CCYYMMDDhhmmss. 00385 * 00386 * @param fDTG 00387 * The string to parse 00388 * @param year 00389 * Upon successful return, this contains the year (including century) from the string. 00390 * @param month 00391 * Upon successful return, this contains the month from the string. 00392 * @param day 00393 * Upon successful return, this contains the day from the string. 00394 * @param hour 00395 * Upon successful return, this contains the hours from the string. 00396 * @param min 00397 * Upon successful return, this contains the minutes from the string. 00398 * @param sec 00399 * Upon successful return, this contains the seconds from the string. 00400 * @param pDateValid 00401 * If non-NULL, the value pointed at will contain true if the date portion 00402 * of the date-time string parsed successfully. 00403 * @param pTimeValid 00404 * If non-NULL, the value pointed at will contain true if the time portion 00405 * of the date-time string parsed successfully. 00406 * 00407 * @return True if the parse completely succeeded, false otherwise. Note 00408 * that even if this returns false, pDateValid and pTimeValid will 00409 * be set appropriately. 00410 */ 00411 bool DtgParseCCYYMMDDhhmmss(const std::string& fDTG, unsigned short& year, unsigned short& month, 00412 unsigned short& day, unsigned short& hour, unsigned short& min, unsigned short& sec, bool* pDateValid = NULL, 00413 bool* pTimeValid = NULL); 00414 00415 /** 00416 * Parse a date-time string of the form CCYYMMDDhhmmss. 00417 * 00418 * @param fDTG 00419 * The string to parse 00420 * @param pDateTime 00421 * Upon successful return, this contains the date and time from the string. 00422 * 00423 * @return True if the parse succeeded, false otherwise. 00424 * Note that even if this returns false, pDateTime may contain partially valid data. 00425 */ 00426 bool DtgParseCCYYMMDDhhmmss(const std::string& fDTG, DateTime* pDateTime); 00427 00428 /** 00429 * Parse a date-time string of the form CCYYMMDD. 00430 * 00431 * @param fDTG 00432 * The string to parse 00433 * @param year 00434 * Upon successful return, this contains the year (including century) from the string. 00435 * @param month 00436 * Upon successful return, this contains the month from the string. 00437 * @param day 00438 * Upon successful return, this contains the day from the string. 00439 * 00440 * @return True if the parse succeeded, false otherwise. 00441 */ 00442 bool DtgParseCCYYMMDD(const std::string& fDTG, unsigned short& year, unsigned short& month, unsigned short& day); 00443 00444 /** 00445 * Parse a date-time string of the form CCYYMMDD. 00446 * 00447 * @param fDTG 00448 * The string to parse 00449 * @param pDateTime 00450 * Upon successful return, this contains the date and time from the string. 00451 * 00452 * @return True if the parse succeeded, false otherwise. 00453 */ 00454 bool DtgParseCCYYMMDD(const std::string& fDTG, DateTime* pDateTime); 00455 00456 /** 00457 * Parse a date-time string of the form DDMMMYY, where MMM is a three-letter 00458 * abbreviation for the month. 00459 * 00460 * @param fDTG 00461 * The string to parse 00462 * @param year 00463 * Upon successful return, this contains the year (including century) from the string. 00464 * The century assumes that any two-digit year less than 48 refers to 2000 + YY, and any 00465 * two-digit year greater than or equal to 48 refers to 1900 + YY. 00466 * @param month 00467 * Upon successful return, this contains the 1-based month from the string. 00468 * @param day 00469 * Upon successful return, this contains the day from the string. 00470 * 00471 * @return True if the parse succeeded, false otherwise. 00472 */ 00473 bool DtgParseDDMMMYY(const std::string& fDTG, unsigned short& year, unsigned short& month, unsigned short& day); 00474 00475 /** 00476 * Parse a date-time string of the form DDMMMYY, where MMM is a three-letter 00477 * abbreviation for the month. 00478 * 00479 * @param fDTG 00480 * The string to parse 00481 * @param pDateTime 00482 * Upon successful return, this contains the date and time from the string. 00483 * The century assumes that any two-digit year less than 48 refers to 2000 + YY, and any 00484 * two-digit year greater than or equal to 48 refers to 1900 + YY. 00485 * 00486 * @return True if the parse succeeded, false otherwise. 00487 */ 00488 bool DtgParseDDMMMYY(const std::string& fDTG, DateTime* pDateTime); 00489 00490 /** 00491 * Parse a date-time string of the form CCYYMMDDhhmmss. 00492 * 00493 * @param fDTG 00494 * The string to parse 00495 * @param year 00496 * Upon successful return, this contains the year (including century) from the string. 00497 * @param month 00498 * Upon successful return, this contains the month from the string. 00499 * @param day 00500 * Upon successful return, this contains the day from the string. 00501 * @param hour 00502 * Upon successful return, this contains the hours from the string. 00503 * @param min 00504 * Upon successful return, this contains the minutes from the string. 00505 * @param sec 00506 * Upon successful return, this contains the seconds from the string. 00507 * 00508 * @return True if the parse succeeded, false otherwise. 00509 */ 00510 bool DtgParseCCYYMMMDDhhmmss(const std::string& fDTG, unsigned short& year, unsigned short& month, 00511 unsigned short& day, unsigned short& hour, unsigned short& min, unsigned short& sec); 00512 00513 /** 00514 * Parse a date-time string of the form CCYYMMDDhhmmss. 00515 * 00516 * @param fDTG 00517 * The string to parse 00518 * @param pDateTime 00519 * Upon successful return, this contains the date and time from the string. 00520 * 00521 * @return True if the parse succeeded, false otherwise. 00522 */ 00523 bool DtgParseCCYYMMMDDhhmmss(const std::string& fDTG, DateTime* pDateTime); 00524 00525 /** 00526 * Parse a date-time string of the form DDHHMMSSZMONYY, where MMM is a three-letter 00527 * abbreviation for the month. 00528 * 00529 * @param fDTG 00530 * The string to parse 00531 * @param year 00532 * Upon successful return, this contains the year (including century) from the string. 00533 * The century assumes that any two-digit year less than 48 refers to 2000 + YY, and any 00534 * two-digit year greater than or equal to 48 refers to 1900 + YY. 00535 * @param month 00536 * Upon successful return, this contains the month from the string. 00537 * @param day 00538 * Upon successful return, this contains the day from the string. 00539 * @param hour 00540 * Upon successful return, this contains the hours from the string. 00541 * @param min 00542 * Upon successful return, this contains the minutes from the string. 00543 * @param sec 00544 * Upon successful return, this contains the seconds from the string. 00545 * 00546 * @return True if the parse succeeded, false otherwise. 00547 */ 00548 bool DtgParseDDHHMMSSZMONYY(const std::string& fDTG, unsigned short& year, unsigned short& month, 00549 unsigned short& day, unsigned short& hour, unsigned short& min, unsigned short& sec); 00550 00551 /** 00552 * Parse a date-time string of the form CCYYMMDDhhmmss, where MMM is a three-letter 00553 * abbreviation for the month. 00554 * 00555 * @param fDTG 00556 * The string to parse 00557 * @param pDateTime 00558 * Upon successful return, this contains the date and time from the string. 00559 * The century assumes that any two-digit year less than 48 refers to 2000 + YY, and any 00560 * two-digit year greater than or equal to 48 refers to 1900 + YY. 00561 * 00562 * @return True if the parse succeeded, false otherwise. 00563 */ 00564 bool DtgParseDDHHMMSSZMONYY(const std::string& fDTG, DateTime* pDateTime); 00565 00566 /** 00567 * Parse a date-time string of the form YYMMDD. 00568 * 00569 * @param fDTG 00570 * The string to parse. 00571 * @param year 00572 * Upon successful return, this contains the year (including century) from the string. 00573 * The century assumes that any two-digit year less than 48 refers to 2000 + YY, and any 00574 * two-digit year greater than or equal to 48 refers to 1900 + YY. 00575 * @param month 00576 * Upon successful return, this contains the month from the string. 00577 * @param day 00578 * Upon successful return, this contains the day from the string. 00579 * 00580 * @return True if the parse succeeded, false otherwise. 00581 */ 00582 bool DtgParseYYMMDD(const std::string& fDTG, unsigned short& year, unsigned short& month, unsigned short& day); 00583 00584 /** 00585 * Parse a date-time string of the form YYMMDD. 00586 * 00587 * @param fDTG 00588 * The string to parse. 00589 * @param pDateTime 00590 * Upon successful return, this contains the date from the string. 00591 * The century assumes that any two-digit year less than 48 refers to 2000 + YY, and any 00592 * two-digit year greater than or equal to 48 refers to 1900 + YY. 00593 * 00594 * @return True if the parse succeeded, false otherwise. 00595 */ 00596 bool DtgParseYYMMDD(const std::string& fDTG, DateTime* pDateTime); 00597 00598 /** 00599 * Test a tag to ensure valid BCS-A strings from within a set. 00600 * 00601 * @param testValue 00602 * String to test. 00603 * @param reporter 00604 * ostream to report errors to. 00605 * @param testSet 00606 * Set of always allowed strings. 00607 * @param allBlankOk 00608 * If true, it is valid to have an all blank (whitespace) string in testValue. 00609 * @param notInSetOk 00610 * If true, it is valid to have testValue not occur in testSet. 00611 * @param emitMsgNotInSet 00612 * If true, emit a message to reporter when testValue does not occur in testSet. 00613 * 00614 * @return This method follows these rules, in this order: 00615 * -# If testValue occurs in testSet, returns TreState::VALID. 00616 * -# If true is false, and testString is all blank, 00617 * emits a message and returns TreState::INVALID. 00618 * -# If emitMsgNotInSet, emit a message 00619 * -# If notInSetOk is false, returns TreState::SUSPECT. 00620 * -# If testValue contains characters outside of BCS-A, 00621 * emits a message and returns TreState::INVALID. 00622 */ 00623 TreState testTagValidBcsASet(const std::string& testValue, std::ostream& reporter, std::set<std::string> testSet, 00624 bool allBlankOk = false, bool notInSetOk = true, bool emitMsgNotInSet = false); 00625 00626 /** 00627 * Test a tag to ensure valid BCS-A strings from within a set. 00628 * 00629 * @param tre 00630 * DynamicObject to get the test value from 00631 * @param reporter 00632 * ostream to report errors to. 00633 * @param pNumFields 00634 * Number of fields currently read in by the calling parser. 00635 * If not NULL, this value is incremented if the field has been read. 00636 * @param name 00637 * The name of the test value within the DynamicObject. 00638 * @param testSet 00639 * Set of always allowed strings. 00640 * @param allBlankOk 00641 * If true, it is valid to have an all blank (whitespace) string in the test value. 00642 * @param notInSetOk 00643 * If true, it is valid to have the test value not occur in testSet. 00644 * @param emitMsgNotInSet 00645 * If true, emit a message to reporter when the test value does not occur in testSet. 00646 * 00647 * @return This method follows these rules, in this order: 00648 * -# If testValue occurs in testSet, returns TreState::VALID. 00649 * -# If true is false, and testString is all blank, 00650 * emits a message and returns TreState::INVALID. 00651 * -# If emitMsgNotInSet, emit a message 00652 * -# If notInSetOk is false, returns TreState::SUSPECT. 00653 * -# If testValue contains characters outside of BCS-A, 00654 * emits a message and returns TreState::INVALID. 00655 */ 00656 TreState testTagValidBcsASet(const DynamicObject& tre, std::ostream& reporter, unsigned int* pNumFields, 00657 const std::string& name, const std::set<std::string>& testSet, bool allBlankOk = false, 00658 bool notInSetOk = true, bool emitMsgNotInSet = false); 00659 00660 /** 00661 * Reads a number of characters from a stream and places it in a vector of chars. 00662 * 00663 * @param strm 00664 * The source stream. 00665 * @param buf 00666 * The destination vector for the data. 00667 * @param count 00668 * The number of bytes to read from the stream. 00669 * @param bStr 00670 * TRUE if the value read will need to be a NULL-terminated string. FALSE if the data will be 00671 * treated as binary data. 00672 * @return TRUE if the operation succeeds and the stream is good, FALSE otherwise. 00673 */ 00674 bool readFromStream(std::istream& strm, std::vector<char>& buf, std::streamsize count, bool bStr = true); 00675 00676 /** 00677 * Cleans up a string for NITF export. It guarantees the string is characters long. 00678 * Left or right justified, as specified, if the string needs to be lengthened. 00679 * 00680 * @param str 00681 * The string to be cleaned up for NITF export 00682 * @param size 00683 * The required length of the output string. 00684 * @param fillChar 00685 * The character to fill with if the string needs to be lenghtened. Default is blank (' '). 00686 * @param rightJustify 00687 * If true then the string is right justified, left justified otherwise. 00688 * @return The output string. Guaranteed to be size characters long. 00689 */ 00690 std::string sizeString(const std::string& str, unsigned int size, char fillChar = ' ', bool rightJustify = false); 00691 00692 /** 00693 * Converts a numeric to a string. It only works with numeric intrinsics 00694 * Guaranteed to be num characters long with a leading '-' if negative or 00695 * a leading '+' if positive and posSign == true 00696 * 00697 * @param num 00698 * The numeric to be converted to a string. 00699 * @param size 00700 * The required length of the output string. 00701 * @param precision 00702 * For floating point the number of digits after the decimal point. 00703 * Default == -1 which means to fit in the maximum within the allocated size. 00704 * @param fillChar 00705 * The numeric fill character. Default == '0'. 00706 * @param posSign 00707 * If true then output a leading "+" if the num is positive. 00708 * @param sciNotation 00709 * If true then force scientific notation in the form: "+0.123456E+001" 00710 * @param expSize 00711 * The number of digits in the exponent (where 1 <= expSize <= 3). 00712 * @param optional 00713 * \c True if field can be exported as optional (with placeholders) if not present initially. 00714 * @param optionalChar 00715 * The \c char to use when filling the field with a placeholder. 00716 * @return The numeric value converted to a string. Guaranteed to be num characters 00717 * long with a leading '-' if negative or a leading '+' if positive and posSign == true 00718 */ 00719 template<typename T> 00720 std::string toString(T num, unsigned int size, int precision = -1, char fillChar = '0', bool posSign = false, 00721 bool sciNotation = false, int expSize = 3, bool optional = false, char optionalChar = ' ') 00722 { 00723 std::stringstream outp; 00724 std::string outStr; 00725 00726 if (optional == true && getSentinel<T>() == num) 00727 { 00728 outStr.resize(size, optionalChar); 00729 return outStr; 00730 } 00731 00732 if (precision < 0) 00733 { 00734 precision = size; 00735 } 00736 00737 if (posSign) 00738 { 00739 outp << std::showpos; 00740 } 00741 00742 if (fillChar == '0') 00743 { 00744 outp << std::internal; 00745 } 00746 00747 outp << std::setfill(fillChar) << std::noboolalpha << std::setw(size); 00748 00749 if (typeid(num) == typeid(float) || typeid(num) == typeid(double)) 00750 { 00751 if (sciNotation) 00752 { 00753 // The I/O routines give us a 3 digit exponent but some TAGs require less. 00754 // The minimum size is 7, +0.0E+0. Addition digits are added to either the 00755 // precision of the mantissia or exponent. +0.0000E+000 00756 if (size < 7) 00757 { 00758 sciNotation = false; 00759 } 00760 else 00761 { 00762 if (expSize < 1 || expSize > 3) 00763 { 00764 // We need the abs(num) but the overloaded abs() function is ambiguous 00765 // in this template so use "?" operator on a known type 00766 double ans = static_cast<double>(num); 00767 ans = (ans >= 0 ? ans : -ans); 00768 00769 expSize = 3; 00770 if (ans < 1.0E+10) 00771 { 00772 expSize = 1; 00773 } 00774 else if (ans > 1.0E-99 && ans < 1.0E+100) 00775 { 00776 expSize = 2; 00777 } 00778 00779 } 00780 int prec = size - 5 - expSize; 00781 if (prec == -1) 00782 { 00783 prec = 1; 00784 expSize = 1; 00785 } 00786 else if (prec == 0) 00787 { 00788 prec = 1; 00789 expSize = 2; 00790 } 00791 00792 outp.setf(std::ios::uppercase); 00793 outp << std::scientific << std::setprecision(prec) << num; 00794 } 00795 } 00796 00797 if (!sciNotation) 00798 { 00799 outp << std::fixed << std::setprecision(precision) << num; 00800 } 00801 } 00802 00803 // char types print out as the ASCII value instead of the numeric 00804 // so cast it to an int. 00805 else if (typeid(num) == typeid(char) || 00806 typeid(num) == typeid(unsigned char) || typeid(num) == typeid(signed char)) 00807 { 00808 outp << static_cast<int>(num); 00809 } 00810 00811 // "unsigned" vars do not print the "+" even with "showpos" 00812 // so cast it to a signed int to force the "+" 00813 // then use a int64 for the extra room to force it positive even 00814 // if the sign bit was set. 00815 else if (!std::numeric_limits<T>::is_signed) 00816 { 00817 outp << static_cast<long long>(num); 00818 } 00819 else 00820 { 00821 outp << num; 00822 } 00823 00824 // use ".str()" method instead of ">>" because ">>" left justifies 00825 // blank filled strings whereas ".str()" works in every case 00826 outStr = outp.str(); 00827 00828 if (sciNotation) 00829 { 00830 // Find the first digit in the exponent 00831 const unsigned int eLocation = outStr.rfind("E"); 00832 VERIFYRV(eLocation != std::string::npos && eLocation < outStr.length() - 2, std::string()); 00833 const unsigned int exponentLocation = eLocation + 2; 00834 00835 // If there are not enough digits, add them 00836 while (outStr.length() - exponentLocation < static_cast<unsigned int>(expSize)) 00837 { 00838 outStr.insert(exponentLocation, "0"); 00839 } 00840 00841 // If there are too many digits, delete them 00842 while (outStr.length() - exponentLocation > static_cast<unsigned int>(expSize)) 00843 { 00844 outStr.erase(exponentLocation, 1); 00845 } 00846 } 00847 00848 // clamp the string length to the required size because << might have 00849 // made it too long. This will truncate the least significant digits. 00850 if (outStr.size() != size) 00851 { 00852 outStr.resize(size); 00853 } 00854 00855 return outStr; 00856 } 00857 00858 00859 // General template for dealing with most types 00860 template<typename T> 00861 inline T fromBuffer(std::vector<char>& buf, bool& ok, bool allBlankOk, bool allDashesOk) 00862 { 00863 std::string trimmedString = StringUtilities::stripWhitespace(std::string(&buf.front())); 00864 00865 // This special check must be made for signed types because boost::lexical_cast accepts 00866 // strings starting with a minus sign for unsigned types and converts them to two's complement. 00867 // See http://www.boost.org/doc/libs/1_47_0/libs/conversion/lexical_cast.htm#faq for more details. 00868 if (!std::numeric_limits<T>::is_signed) 00869 { 00870 if (trimmedString.empty() == false && trimmedString[0] == '-') 00871 { 00872 // This could be all dashes or a negative number. 00873 std::string allDashes(trimmedString.size(), '-'); 00874 if (allDashesOk == false || trimmedString != allDashes) 00875 { 00876 ok = false; 00877 } 00878 00879 return getSentinel<T>(); 00880 } 00881 } 00882 00883 try 00884 { 00885 if (trimmedString.empty() == true) 00886 { 00887 ok = allBlankOk; 00888 } 00889 else 00890 { 00891 if (allDashesOk) 00892 { 00893 std::string allDashes(trimmedString.size(), '-'); 00894 if (trimmedString == allDashes) 00895 { 00896 return getSentinel<T>(); 00897 } 00898 } 00899 00900 return boost::lexical_cast<T>(trimmedString); 00901 } 00902 } 00903 catch (boost::bad_lexical_cast e) 00904 { 00905 ok = false; 00906 } 00907 00908 return getSentinel<T>(); 00909 } 00910 00911 // Specialization for the unusual ones 00912 template <> 00913 inline std::string fromBuffer<std::string>(std::vector<char>& buf, bool& ok, bool allBlankOk, bool allDashesOk) 00914 { 00915 std::string trimmedString = StringUtilities::stripWhitespace(std::string(&buf.front())); 00916 if (trimmedString.empty() == true) 00917 { 00918 ok = allBlankOk; 00919 } 00920 00921 return trimmedString; 00922 } 00923 00924 // create the error message 00925 inline bool readFieldErrMsg(std::string& msg, const std::string& name, const std::vector<char>& buf, int len) 00926 { 00927 std::string value; 00928 value.resize(len); 00929 strcpy(&value[0], &buf[0]); 00930 00931 msg += "NITF Parse TAG Error: " + name + " = " + value + "\n"; 00932 return true; 00933 } 00934 00935 // 00936 inline bool numReadErrMsg(int64_t numRead, int64_t numBytes, std::string& errorMessage) 00937 { 00938 std::string numB; 00939 std::string numR; 00940 00941 try 00942 { 00943 numR = boost::lexical_cast<std::string>(numRead); 00944 numB = boost::lexical_cast<std::string>(numBytes); 00945 } 00946 catch (boost::bad_lexical_cast e) 00947 { 00948 } 00949 00950 if (numRead < 0) 00951 { 00952 errorMessage += "Read past end of data. Should have read " + numB + " bytes.\n"; 00953 } 00954 else 00955 { 00956 errorMessage += "Read " + numR + " bytes. Should have read " + numB + " bytes.\n"; 00957 } 00958 00959 return true; 00960 } 00961 00962 template<typename T> 00963 inline bool readField(std::istream& input, DynamicObject& output, bool& success, 00964 const std::string& name, int len, std::string& msg, std::vector<char>& buf, 00965 bool allBlankOk = false, bool allDashesOk = false) 00966 { 00967 bool ok(true); 00968 if (input.good() == false) 00969 { 00970 success = false; 00971 } 00972 else if (readFromStream(input, buf, len) == false) 00973 { 00974 success = false; 00975 } 00976 else 00977 { 00978 T value = fromBuffer<T>(buf, ok, allBlankOk, allDashesOk); 00979 if (ok == false || output.setAttribute(name, value) == false) 00980 { 00981 success = false; 00982 readFieldErrMsg(msg, name, buf, len); 00983 } 00984 } 00985 00986 return success; 00987 } 00988 00989 template<typename T> 00990 inline bool readAndConvertFromStream(std::istream& input, T& output, bool& success, 00991 const std::string& name, int len, std::string& msg, std::vector<char>& buf, 00992 bool allBlankOk = false, bool allDashesOk = false) 00993 { 00994 bool ok(true); 00995 if (!input.good()) 00996 { 00997 success = false; 00998 } 00999 else if (!readFromStream(input, buf, len)) 01000 { 01001 success = false; 01002 } 01003 else 01004 { 01005 output = fromBuffer<T>(buf, ok, allBlankOk, allDashesOk); 01006 if (!ok) 01007 { 01008 success = false; 01009 readFieldErrMsg(msg, name, buf, len); 01010 } 01011 } 01012 01013 return success; 01014 } 01015 01016 /** 01017 * Determines whether a Classification object is valid for export to a NITF 2.1 file. 01018 * 01019 * @param classification 01020 * The Classification object to validate. 01021 * 01022 * @param errorMessage 01023 * An error message indicating the nature of the problem. 01024 * 01025 * @return True if the given object is valid for NITF 2.1 export, false otherwise. 01026 */ 01027 bool isClassificationValidForExport(const Classification& classification, std::string& errorMessage); 01028 01029 /** 01030 * Determines whether a classification field is valid for export to a NITF 2.1 file. 01031 * 01032 * @param classification 01033 * The Classification object to validate. 01034 * 01035 * @param fieldName 01036 * The name of the field to validate. 01037 * 01038 * @return True if the given field is valid for NITF 2.1 export, false otherwise. 01039 */ 01040 bool isClassificationFieldValidForExport(const Classification& classification, const std::string& fieldName); 01041 01042 /** 01043 * Updates band information in the special metadata, computing start, 01044 * center, and end wavelengths from the given data if possible. 01045 * 01046 * @param pMetadata 01047 * The metadata to update. 01048 * 01049 * @param centerWavelengths 01050 * The center wavelengths to write or an empty vector if center wavelengths are unknown. 01051 * 01052 * @param startWavelengths 01053 * The start wavelengths to write or an empty vector if start wavelengths are unknown. 01054 * 01055 * @param endWavelengths 01056 * The end wavelengths to write or an empty vector if end wavelengths are unknown. 01057 * 01058 * @param fwhms 01059 * The FWHM for each band or an empty vector if FWHMs are unknown. 01060 * 01061 * @param convertFromInverseCentimeters 01062 * \c True if all data is in inverse centimeters, \c false if all data is in microns. 01063 * 01064 * @return \c True on success, \c false otherwise. 01065 */ 01066 bool updateSpecialMetadata(DynamicObject* pMetadata, std::vector<double>& centerWavelengths, 01067 std::vector<double>& startWavelengths, std::vector<double>& endWavelengths, 01068 const std::vector<double>& fwhms, bool convertFromInverseCentimeters = false); 01069 } 01070 01071 #endif