1 //
2 // This file is part of the SDTS++ toolkit, written by the U.S.
3 // Geological Survey. It is experimental software, written to support
4 // USGS research and cartographic data production.
5 //
6 // SDTS++ is public domain software. It may be freely copied,
7 // distributed, and modified. The USGS welcomes user feedback, but makes
8 // no committment to any level of support for this code. See the SDTS
9 // web site at http://mcmcweb.er.usgs.gov/sdts for more information,
10 // including points of contact.
11 //
12
13 #ifdef WIN32
14 #pragma warning( disable : 4786 )
15 #endif
16
17 #include <string>
18 #include <fstream>
19
20 #include <cstring>
21
22 #include <algorithm>
23 #include <functional>
24
25 #ifndef INCLUDED_SB_UTILS_H
26 #include <sdts++/builder/sb_Utils.h>
27 #endif
28
29 #include <sdts++/builder/sb_Iref.h>
30 #include <sdts++/builder/sb_Catd.h>
31 #include <sdts++/builder/sb_Ddsh.h>
32
33 #include <sdts++/io/sio_ConverterFactory.h>
34 #include <sdts++/io/sio_Reader.h>
35
36 #include "sysutils/fileutils.h"
37 #include "sysutils/stringutils.h"
38
39 using namespace std;
40
41
42 static const char* ident_ =
43 "$Id: sb_Utils.cpp,v 1.16 2002/03/27 23:47:46 mcoletti Exp $";
44
45
46 //
47 // Support Functions for sb_Utils
48 //
49
50
51 // Generic utility things.
52 //
53 // XXX You better believe__ this is temporary.
54
55 // This algorithm is used in find_if calls to locate fields
56 // or subfields that have a given mnemonic.
57
58 // Q.v., sc_Record:getRecordID() for an example of its use.
59
60 template< class T >
61 struct equalMnemonic : binary_function<T, const string, bool>
62 {
operator ()equalMnemonic63 bool operator()( const T& x, const string& y ) const
64 { return x.getMnemonic() == y; }
65
66 };
67 // comparison class equalMnemonic
68
69 template< class T >
70 struct equalName : binary_function<T, const string, bool>
71 {
operator ()equalName72 bool operator()( const T& x, const string& y ) const
73 { return x.getName() == y; }
74
75 };
76 // comparison class equalName
77
78 // This algorithm is used in find_if calls to locate modules with a
79 // given module type.
80
81 template< class T >
82 struct equalModuleType : binary_function<T, const string, bool>
83 {
operator ()equalModuleType84 bool operator()( const T& x, const string& y ) const
85 { return x.getModuleType() == y; }
86
87 };
88 // comparison class equalModuleType
89
90
91
92 /*
93 * Implementation of the sb_Utils functions.
94 */
95
96 bool
getFieldByMnem(sc_Record const & rec,string const & mnemonic,sc_Record::const_iterator & thefield)97 sb_Utils::getFieldByMnem(sc_Record const& rec,
98 string const& mnemonic,
99 sc_Record::const_iterator& thefield)
100 {
101 thefield = find_if( rec.begin(), rec.end(),
102 bind2nd(equalMnemonic<sc_Field>(), mnemonic) );
103 if ( thefield == rec.end() )
104 return false;
105
106 return true;
107 }
108
109
110
111
112 bool
getSubfieldByMnem(sc_Field const & field,string const & mnemonic,sc_Field::const_iterator & thesubf)113 sb_Utils::getSubfieldByMnem( sc_Field const & field,
114 string const & mnemonic,
115 sc_Field::const_iterator & thesubf )
116 {
117 thesubf = find_if( field.begin(), field.end(),
118 bind2nd(equalMnemonic<sc_Subfield>(), mnemonic) );
119
120 if ( thesubf == field.end() )
121 { return false; }
122
123 return true;
124
125 } // sb_Utils::getSubfieldByMnem
126
127
128
129
130 bool
getSubfieldByName(sc_Field const & field,string const & name,sc_Field::const_iterator & thesubf)131 sb_Utils::getSubfieldByName( sc_Field const& field,
132 string const& name,
133 sc_Field::const_iterator& thesubf )
134 {
135 thesubf = find_if( field.begin(), field.end(),
136 bind2nd(equalName<sc_Subfield>(), name) );
137
138 if ( thesubf == field.end() )
139 { return false; }
140
141 return true;
142
143 } // sb_Utils::getSubfieldByName
144
145
146
147 // Tries to convert a subfield into a double.
148 // If it succeeds returns True, else False.
149 // Place the convert value into the dataTo passed in parameter
150 bool
getDoubleFromSubfield(sc_SubfieldCntr::const_iterator const & subf,double & dataOut)151 sb_Utils::getDoubleFromSubfield(sc_SubfieldCntr::const_iterator const& subf, //sc_Subfield const& subf,
152 double& dataOut )
153 {
154
155 bool rc;
156 unsigned long tempULong;
157 long tempLong;
158 sc_Subfield::SubfieldType sType = subf->getSubfieldType();
159
160 switch(sType) {
161
162 // Not supported at this time.
163 // convert string class to double
164 case(sc_Subfield::is_A):
165 case(sc_Subfield::is_C):
166 return false;
167 break;
168
169 // convert long to double
170 case(sc_Subfield::is_I):
171 rc = subf->getI( tempLong );
172 dataOut = double(tempLong);
173 break;
174 case(sc_Subfield::is_BI8):
175 rc = subf->getBI8( tempLong );
176 dataOut = double(tempLong);
177 break;
178 case(sc_Subfield::is_BI16):
179 rc = subf->getBI16( tempLong );
180 dataOut = double(tempLong);
181 break;
182 case(sc_Subfield::is_BI24):
183 rc = subf->getBI24( tempLong );
184 dataOut = double(tempLong);
185 break;
186 case(sc_Subfield::is_BI32):
187 rc = subf->getBI32( tempLong );
188 dataOut = double(tempLong);
189 break;
190
191 // Convert unsigned long to double
192 case(sc_Subfield::is_BUI8):
193 rc = subf->getBUI8( tempULong );
194 dataOut = double(tempULong );
195 break;
196 case(sc_Subfield::is_BUI16):
197 rc = subf->getBUI16( tempULong );
198 dataOut = double(tempULong );
199 break;
200 case(sc_Subfield::is_BUI24):
201 rc = subf->getBUI24( tempULong );
202 dataOut = double(tempULong );
203 break;
204 case(sc_Subfield::is_BUI32):
205 rc = subf->getBUI32( tempULong );
206 dataOut = double(tempULong );
207 break;
208
209 //convert double to double
210 case(sc_Subfield::is_R):
211 rc = subf->getR( dataOut );
212 break;
213 case(sc_Subfield::is_S):
214 rc = subf->getS( dataOut );
215 break;
216
217 // non-implimented return false
218 case(sc_Subfield::is_B):
219 case(sc_Subfield::is_BUI):
220 case(sc_Subfield::is_BFP32):
221 case(sc_Subfield::is_BFP64):
222 rc = false;
223 break;
224
225 // Shouldn't get here, except in an error situation
226 default:
227 rc = false;
228 }
229
230 return rc;
231
232
233 }
234 /* Numberic -> optionalSign Digits FrationalPart ExponentPart
235 */
236 /*
237 *
238 * <number> -> [ <sign> ] <digit> { <digit> } [ <deciaml> ] [ <exponent> ]
239 *
240 * <sign> -> '+' | '-'
241 *
242 * <decimal> -> '.' { <digit> }
243 *
244 * <exponent> -> 'E' [ <sign> ] <digit> { <digit> }
245 *
246 * <digits> -> 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0
247 *
248 */
249
250 // bool String2double(sc_SubfieldCntr::const_iterator const& subf, double &dataOut );
251
252 inline
253 bool
String2double(sc_SubfieldCntr::const_iterator const & subf,double & dataOut)254 String2double( sc_SubfieldCntr::const_iterator const& subf,
255 double &dataOut )
256 {
257 sc_Subfield::SubfieldType sType = subf->getSubfieldType();
258 string tempString;
259 bool rc;
260 switch(sType){
261 case(sc_Subfield::is_A):
262 rc = subf->getA( tempString );
263 break;
264 case(sc_Subfield::is_C):
265 rc = subf->getC( tempString );
266 break;
267 default:
268 // Error!
269 // Illegal subfield type passed to function.
270 return false;
271 }
272 if ( rc == false ) // get on subfield failed pass it on.
273 return false;
274 // Verfiy string is only digits,
275 // convert
276 return true;
277 }
278
279
280
281 void
add_field(sc_Record & record,std::string const & name,std::string const & mnemonic)282 sb_Utils::add_field( sc_Record & record,
283 std::string const & name,
284 std::string const & mnemonic )
285 {
286
287 record.push_back( sc_Field() );
288
289 record.back().setName( name );
290
291 record.back().setMnemonic( mnemonic );
292
293 } // add_field
294
295
296
297 void
add_foreignID(sc_Record & record,sb_ForeignID const & frid)298 sb_Utils::add_foreignID( sc_Record & record,
299 sb_ForeignID const & frid )
300 {
301 // first add the field
302 sb_Utils::add_field( record, frid.name(), frid.mnemonic() );
303
304 // now add the relevent subfields
305
306 sb_Utils::add_subfield( record.back(),
307 "MODN",
308 frid.moduleName() );
309
310 sb_Utils::add_subfield( record.back(),
311 "RCID",
312 frid.recordID() );
313
314 // XXX add support for optional usage modifier
315
316 } // sb_Utils::add_foreignID( sc_Record & record,
317
318
319
320 /// XXX Should sc_Field have an addSubfield that does this instead?
321 void
add_subfield(sc_Field & field,string const & mnemonic,string const & value)322 sb_Utils::add_subfield( sc_Field& field,
323 string const& mnemonic,
324 string const& value )
325 { field.push_back( sc_Subfield() );
326
327 field.back().setMnemonic( mnemonic );
328 field.back().setA( value );
329 } // add_subfield
330
331
332
333 void
add_subfield(sc_Field & field,string const & mnemonic,int value)334 sb_Utils::add_subfield( sc_Field& field, string const& mnemonic, int value )
335 {
336 field.push_back( sc_Subfield() );
337
338 field.back().setMnemonic( mnemonic );
339 field.back().setI( (long) value );
340 } // add_subfield
341
342
343
344 void
add_subfield(sc_Field & field,string const & mnemonic,long value)345 sb_Utils::add_subfield( sc_Field& field, string const& mnemonic, long value )
346 {
347 field.push_back( sc_Subfield() );
348
349 field.back().setMnemonic( mnemonic );
350 field.back().setI( value );
351 } // add_subfield
352
353
354
355 void
add_subfield(sc_Field & field,string const & mnemonic,double value)356 sb_Utils::add_subfield( sc_Field& field, string const& mnemonic, double value )
357 {
358 field.push_back( sc_Subfield() );
359
360 field.back().setMnemonic( mnemonic );
361 field.back().setR( value );
362 } // add_subfield
363
364
365
366
367 void
add_empty_subfield(sc_Field & field,string const & mnemonic,sc_Subfield::SubfieldType type)368 sb_Utils::add_empty_subfield( sc_Field& field,
369 string const& mnemonic,
370 sc_Subfield::SubfieldType type )
371 {
372 field.push_back( sc_Subfield() );
373
374 field.back().setMnemonic( mnemonic );
375
376 // XXX this switch statement could be
377 // XXX eliminated if the setUnvalued()
378 // XXX took a type parameter, or was
379 // XXX defined for each subfield type
380 switch ( type )
381 {
382 case sc_Subfield::is_A :
383 field.back().setA( "" );
384 break;
385 case sc_Subfield::is_I :
386 field.back().setI( (long) 0 );
387 break;
388 case sc_Subfield::is_R :
389 field.back().setR( 0.0 );
390 break;
391 case sc_Subfield::is_S :
392 field.back().setS( 0.0 );
393 break;
394 default :
395 // non of the other fields can be variable, so this function is irrelevent
396 // (or they're inherently bogus, like the 'C' type subfields
397 break;
398 }
399
400 field.back().setUnvalued(); // explicitly state that it ain't got nothin'
401
402 } // add_subfield
403
404
405
406
407
408 bool
valid_domain(string const & str,string const & values)409 sb_Utils::valid_domain( string const & str, string const & values )
410 {
411 if ( str.length() != 1 ) return false; // single character comparison only
412
413 if ( strpbrk( str.c_str(), values.c_str() ) == NULL )
414 return false;
415
416 return true;
417 } // valid_domain
418
419
420
421
422 // XXX These two could probably be made a template function of some sort.
423
424 bool
valid_domain(long val,set<long> const & domain_values)425 sb_Utils::valid_domain( long val, set<long> const & domain_values )
426 {
427 set<long> tmp_set;
428 tmp_set.insert( val );
429 return includes( domain_values.begin(), domain_values.end(),
430 tmp_set.begin(), tmp_set.end() );
431 } // valid_domain
432
433
434
435
436
437
438 bool
valid_domain(string const & val,set<string> const & domain_values)439 sb_Utils::valid_domain( string const& val, set<string> const & domain_values )
440 {
441 for ( set<string>::const_iterator i = domain_values.begin();
442 i != domain_values.end();
443 i++ )
444 {
445 // I had to use std::lexicographical_compare because "val" was
446 // getting set into a weird state in sb_Ddsh.cpp; NOTHING I did
447 // seemed to correct the problem. E.g., "ATPR" compared to
448 // "ATPR" would indicate they WEREN'T equal.
449 if ( lexicographical_compare( i->begin(), i->end(),
450 val.begin(), val.end() ) )
451 { return true; }
452 }
453
454 return false;
455
456 } // valid_domain
457
458
459
460 void
find(sc_Module::const_iterator begin,sc_Module::const_iterator end,string const & field_name,sc_Subfield const & subfield,sc_Module & matches)461 sb_Utils::find( sc_Module::const_iterator begin,
462 sc_Module::const_iterator end,
463 string const & field_name,
464 sc_Subfield const & subfield,
465 sc_Module & matches )
466 {
467 sc_Record::const_iterator matched_field;
468 sc_Field::const_iterator matched_subfield;
469
470 for ( sc_Module::const_iterator current_record = begin;
471 current_record != end;
472 ++current_record )
473 {
474 if ( getFieldByMnem( *current_record,
475 field_name,
476 matched_field ) )
477 {
478 if ( getSubfieldByMnem( *matched_field,
479 subfield.mnemonic(),
480 matched_subfield ) )
481 {
482 if ( *matched_subfield == subfield )
483 {
484 matches.push_back( *current_record );
485 }
486 }
487 }
488 else
489 {
490 // If the field doesn't exist in one record, it's not going
491 // to magically appear in the rest of the module SDTS records
492 // because these records are homogenous; so why waste time
493 // grinding through the rest of them? So, leave immediately
494 // if the field isn't found in this module record.
495 return;
496 }
497 }
498 } // sb_Utils::find
499
500
501
502
503 /// Returns true if ``type'' is one of the 8211 binary types; otherwise false.
504 /**
505 It does so by the simplest form of checking: since ALL the binary
506 types begin with a 'B' (or 'b'), then check that the first
507 character is that; if so, then it's a binary type, otherwise it
508 can't be. Obviously this will miss malformed binary types strings;
509 e.g., "BAZ". But the sio_ConverterFactory will just return NULL if
510 you try to use such a malformed string, so we needn't worry about
511 being pedantic here.
512 */
513 static
514 bool
isBinaryType_(string const & type)515 isBinaryType_( string const & type )
516 {
517 if ( type[0] == 'b' || type[0] == 'B' )
518 {
519 return true;
520 }
521
522 return false;
523 } // isBinaryType_
524
525
526
527 bool
addConverter(sb_Iref const & iref,sio_8211_converter_dictionary & dictionary)528 sb_Utils::addConverter( sb_Iref const & iref,
529 sio_8211_converter_dictionary & dictionary )
530 {
531 string hfmt; // a dataset's horizontal format description
532
533 if ( ! iref.getHFMT( hfmt ) )
534 {
535 return false;
536 }
537
538 // if it's not a binary type, we don't have to worry about making a
539 // special converter for it; since this isn't exactly an error,
540 // we'll return ``true''. (It's not an error because there are
541 // many instances where the HFMT is non-binary for perfectly
542 // legitimate datasets.)
543 if ( ! isBinaryType_( hfmt ) )
544 {
545 return true;
546 }
547
548 // Ok, so now we've got the HFMT; we merely ask the factory to give
549 // us an appropriate converter and we add that to the dictionary.
550 // Done!
551
552 sio_8211Converter * converter;
553
554 if ( ( converter = sio_ConverterFactory::instance()->get( hfmt ) ) )
555 {
556 // all horizontal format subfield values use "X" and "Y"
557 // mnemonics (I think. At least from what I've seen so far.)
558
559 dictionary[ "X" ] = converter;
560 dictionary[ "Y" ] = converter;
561
562 return true;
563 }
564
565 // We got a binary type but the factory was unable to properly
566 // parse the binary type string, so it's more than likely
567 // malformed, so complain.
568
569 return false;
570
571 } // sb_Utils:: addConverter
572
573
574
575 bool
addConverter(sb_Ddsh const & ddsh,sio_8211_converter_dictionary & dictionary)576 sb_Utils::addConverter( sb_Ddsh const & ddsh,
577 sio_8211_converter_dictionary & dictionary )
578 {
579 string fmt; // a format description
580
581 if ( ! ddsh.getFMT( fmt ) )
582 {
583 return false;
584 }
585
586 // if it's not a binary type, we don't have to worry about making a
587 // special converter for it; since this isn't exactly an error,
588 // we'll return ``true''. (It's not an error because there are
589 // many instances where the FMT is non-binary for perfectly
590 // legitimate datasets.)
591 if ( ! isBinaryType_( fmt ) )
592 {
593 return true;
594 }
595
596 // Ok, so now we've got the FMT; we merely ask the factory to give
597 // us an appropriate converter and we add that to the dictionary.
598 // Done!
599
600 sio_8211Converter * converter;
601
602 if ( ( converter = sio_ConverterFactory::instance()->get( fmt ) ) )
603 {
604 // the binary converter will be keyed by the attribute name
605
606 string attribute_name;
607
608 if ( ! ddsh.getATLB( attribute_name ) )
609 { // Hmm, no attribute? No converter for you!
610 return false;
611 }
612
613 stringutils::chomp( attribute_name ); // nuke any trailing whitespace
614
615 dictionary[ attribute_name ] = converter;
616
617 return true;
618 }
619
620 // We got a binary type but the factory was unable to properly
621 // parse the binary type string, so it's more than likely
622 // malformed, so complain.
623
624 return false;
625
626 } // sb_Utils::addConverter( sb_Ddsh const & ddsh,
627
628
629
630
631 static
632 bool
addConvertersFromIREF_(string const & iref_filename,sio_8211_converter_dictionary & dictionary)633 addConvertersFromIREF_( string const & iref_filename,
634 sio_8211_converter_dictionary & dictionary )
635 {
636 // open the IREF module
637
638 ifstream iref_file( iref_filename.c_str() );
639
640 if ( ! iref_file )
641 { // try giving us a valid file next time, ok?
642 return false;
643 }
644
645 // Now grind through IREF records, calling addConverter() for @ one
646 // (There SHOULD only be ONE IREF record, so the while is
647 // admittedly superfluous; but it's here in case, well, there's
648 // more than one.)
649
650 sio_8211Reader reader( iref_file ); // IREF module reader
651
652 sio_8211ForwardIterator i( reader ); // used to grind through IREF
653
654 sc_Record record; // current IREF record
655
656 sb_Iref iref_module_record;
657
658 while ( i )
659 {
660 i.get( record );
661
662 if ( ! iref_module_record.setRecord( record ) )
663 {
664 return false; // unable to translate IREF record, so leave
665 }
666
667 if ( ! sb_Utils::addConverter( iref_module_record, dictionary ) )
668 {
669 return false;
670 }
671
672 ++i;
673
674 } // grind to next IREF module record
675
676 return true;
677
678 } // addConvertersFromIREF_
679
680
681
682
683 static
684 bool
addConvertersFromDDSH_(string const & ddsh_filename,sio_8211_converter_dictionary & dictionary)685 addConvertersFromDDSH_( string const & ddsh_filename,
686 sio_8211_converter_dictionary & dictionary )
687 {
688 // open the DDSH module
689
690 ifstream ddsh_file( ddsh_filename.c_str() );
691
692 if ( ! ddsh_file )
693 { // try giving us a valid file next time, ok?
694 return false;
695 }
696
697 // start grinding through the records; for each record call
698 // addConverter()
699
700 sio_8211Reader reader( ddsh_file ); // DDSH module reader
701
702 sio_8211ForwardIterator i( reader ); // used to grind through DDSH
703
704 sc_Record record; // current DDSH record
705
706 sb_Ddsh ddsh_module_record;
707
708 while ( i )
709 {
710 i.get( record );
711
712 if ( ! ddsh_module_record.setRecord( record ) )
713 {
714 return false; // unable to translate DDSH record, so leave
715 }
716
717 if ( ! sb_Utils::addConverter( ddsh_module_record, dictionary ) )
718 {
719 return false;
720 }
721
722 ++i;
723
724 } // grind to next DDSH module record
725
726 return true;
727
728 } // addConvertersFromDDSH_
729
730
731
732
733 bool
addConverters(string const & catd_filename,sio_8211_converter_dictionary & dictionary)734 sb_Utils::addConverters( string const & catd_filename,
735 sio_8211_converter_dictionary & dictionary )
736 {
737
738 // save the dir path because we'll need to prepend that to file
739 // names later
740
741 string dir_path( fileutils::dirname( catd_filename ) );
742
743 dir_path += '/';
744
745
746 // open the CATD module
747
748 ifstream catd_file( catd_filename.c_str() );
749
750 if ( ! catd_file )
751 { // try giving us a valid file next time, ok?
752 return false;
753 }
754
755 // start grinding through the records; for each IREF or DDSH hit,
756 // open up _that_ module and start calling addConverter() for @
757 // record
758
759 sio_8211Reader reader( catd_file ); // CATD module reader
760
761 sio_8211ForwardIterator i( reader ); // used to grind through CATD
762
763 sc_Record record; // current CATD record
764
765 string catd_name; // CATD::NAME values
766
767 sb_Catd catd_module_record;
768
769 string file_name; // file name for IREF or DDSH module
770
771 while ( i )
772 {
773 i.get( record );
774
775 if ( ! catd_module_record.setRecord( record ) )
776 {
777 return false; // we have a non CATD module, so bail
778 }
779
780 if ( ! catd_module_record.getNAME( catd_name ) )
781 {
782 return false; // if we can't get CATD::NAME, something's
783 // seriously wrong because ALL CATD modules
784 // have this subfield
785 }
786
787 if ( ! catd_module_record.getFILE( file_name ) )
788 {
789 return false; // dittor for CATD::FILE
790 }
791
792 string full_path_name; // fully qualified file name; needed because
793 // these module may not be in $CWD
794
795 full_path_name = dir_path + file_name;
796
797 if ( "IREF" == catd_name )
798 {
799 if ( ! addConvertersFromIREF_( full_path_name, dictionary ) )
800 {
801 return false;
802 }
803 }
804 else if ( "DDSH" == catd_name )
805 {
806 if ( ! addConvertersFromDDSH_( full_path_name, dictionary ) )
807 {
808 return false;
809 }
810 }
811
812 ++i;
813
814 } // grind to next CATD module record
815
816 return true;
817
818 } // sb_Utils::addConverters
819