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