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 // sio_Reader.cpp
14 //
15 
16 
17 #include <sdts++/io/sio_Reader.h>
18 
19 #include <string>
20 #include <functional>
21 #include <algorithm>
22 #include <map>
23 
24 
25 #ifndef INCLUDED_SIO_8211DIRECTORY_H
26 #include <sdts++/io/sio_8211Directory.h>
27 #endif
28 
29 #ifndef INCLUDED_SIO_8211DIRENTRY_H
30 #include <sdts++/io/sio_8211DirEntry.h>
31 #endif
32 
33 #ifndef INCLUDED_SIO_8211LEADER_H
34 #include <sdts++/io/sio_8211Leader.h>
35 #endif
36 
37 #ifndef INCLUDED_SIO_8211DDRLEADER_H
38 #include <sdts++/io/sio_8211DDRLeader.h>
39 #endif
40 
41 #ifndef INCLUDED_SIO_8211DDR_H
42 #include <sdts++/io/sio_8211DDR.h>
43 #endif
44 
45 #ifndef INCLUDED_SIO_8211DR_H
46 #include <sdts++/io/sio_8211DR.h>
47 #endif
48 
49 #ifndef INCLUDED_SIO_8211DDRFIELD_H
50 #include <sdts++/io/sio_8211DDRField.h>
51 #endif
52 
53 #ifndef INCLUDED_SIO_8211FIELD_H
54 #include <sdts++/io/sio_8211Field.h>
55 #endif
56 
57 #ifndef INCLUDED_SIO_CONVERTER_H
58 #include <sdts++/io/sio_Converter.h>
59 #endif
60 
61 #ifndef INCLUDED_SIO_8211CONVERTER_H
62 #include <sdts++/io/sio_8211Converter.h>
63 #endif
64 
65 #ifndef INCLUDED_SC_RECORD_H
66 #include <sdts++/container/sc_Record.h>
67 #endif
68 
69 
70 #ifdef _MSC_VER
71 #pragma warning(disable:4786)
72 #endif
73 
74 using namespace std;
75 
76 
77 
78 
79 static const char* iden_ = "$Id: sio_Reader.cpp,v 1.21 2003/06/06 21:30:43 mcoletti Exp $";
80 
81 
82 //
83 // The guts for an sio_8211Reader object.
84 //
85 struct sio_8211Reader_Imp
86 {
sio_8211Reader_Impsio_8211Reader_Imp87   sio_8211Reader_Imp( istream& file ) : file_( file ) {}
88 
89 
90   istream& file_;               // stream to an open 8211 file; the user has
91                                 // responsibility of managing this stream
92   sio_8211DDR ddr_;
93 
94   field_format_ctr fieldFormats_;
95 
96   long DRStart_;                // offset for first DR
97 
98 }; // struct sio_8211Reader_Imp
99 
100 
101 
102 
103 
104 
105 //
106 // Read in the 8211 DDR.  After it's read in, the ddr_ object will be
107 // ready for use in reading the 8211's data records.
108 //
109 static
110 bool
readDDR_(sio_8211Reader_Imp & reader_imp,sio_8211_converter_dictionary const * const converters)111 readDDR_( sio_8211Reader_Imp& reader_imp,
112           sio_8211_converter_dictionary const * const converters )
113 {
114   if ( ! reader_imp.file_ ) { return false; }
115 
116   reader_imp.file_ >> reader_imp.ddr_; // slurp in the DDR
117 
118   if ( ! reader_imp.file_ )
119     {
120       return false;
121     }
122 
123   // After we read the DDR, the stream pointer will be sitting at the first
124   // character in the first DR.  Remember that location in DRStart_.
125   reader_imp.DRStart_ = reader_imp.file_.tellg();
126 
127   // Now we need to build the field format "dictionary" that we'll later
128   // use to properly parse a DR.
129 
130   for ( sio_8211Directory::const_iterator dir_entry =
131           reader_imp.ddr_.getDirectory().begin();
132         dir_entry != reader_imp.ddr_.getDirectory().end();
133         dir_entry++ )
134     {
135 
136       if ( (*dir_entry).getTag().substr(0,3) == "000" )
137         {
138           continue;             // this must be a reserved field, so skip it
139         }
140 
141                                 // build a ddr field given the DDR's leader
142                                 // and the current field data
143 
144                                 // XXX This will need to change in the same way
145                                 // XXX that DDRField changed.  That is, we get
146                                 // XXX a leader and explicitly morph it into a
147                                 // XXX DDRLeader.
148 
149       sio_8211DDRLeader const * const ddr_leader =
150         dynamic_cast<sio_8211DDRLeader const * const>(&(reader_imp.ddr_.getLeader()));
151 
152       if ( ! ddr_leader ) return false;
153 
154 
155                                 // XXX for some reason, I can't pass in
156                                 // XXX the results of getField() directory to
157                                 // XXX sio_8211DDRField's ctor.
158 
159       sio_8211Field const * field = (*dir_entry).getField();
160       sio_8211DDRField ddr_field( *ddr_leader, *field );
161 
162 
163       reader_imp.fieldFormats_.push_back( sio_8211FieldFormat() );
164 
165 
166       sio_8211MakeFieldFormat( reader_imp.fieldFormats_.back(),
167                                ddr_field,
168                                (*dir_entry).getTag(),
169                                converters );
170 
171     } // for each DDR field tag
172 
173   return true;
174 
175 } // readDDR_
176 
177 
178 
~sio_8211Reader()179 sio_8211Reader::~sio_8211Reader()
180 {
181   if ( imp_ ) delete imp_;
182 } // sio_8211Reader dtor
183 
184 
185 
186 
187 bool
attach(istream & is,sio_8211_converter_dictionary const * const converters)188 sio_8211Reader::attach( istream & is,
189                         sio_8211_converter_dictionary const * const converters )
190 {
191                                 // create a brand spanking new
192                                 // implementation, giving it the new
193                                 // stream
194   sio_8211Reader_Imp* new_imp = new sio_8211Reader_Imp( is );
195 
196   if ( ! new_imp ) return false;
197 
198                                 // if we have an existing
199                                 // implementation, copy over its
200                                 // stuff and then blow it away
201   if ( imp_ )
202     {
203       new_imp->ddr_ = imp_->ddr_;
204       new_imp->fieldFormats_ = imp_->fieldFormats_;
205       new_imp->DRStart_ = imp_->DRStart_;
206 
207       delete imp_;
208     }
209 
210   imp_ = new_imp;               // now that we have new guts, we need to
211                                 // read in the DDR in preparation for
212                                 // reading records
213 
214   return readDDR_( *imp_, converters );
215 
216 } // sio_8211Reader::attach
217 
218 
219 
220 
221 sio_8211ForwardIterator
begin()222 sio_8211Reader::begin()
223 {
224   return sio_8211ForwardIterator(*this);
225 } // sio_8211Reader::begin
226 
227 
228 
229 
230 field_format_ctr &
getSchema()231 sio_8211Reader::getSchema()
232 {
233   return imp_->fieldFormats_;
234 } // sio_8211Reader::getSchema() const
235 
236 
237 // fills the given sc_field with the subfields found in the given 8211
238 // field
239 //
240 // Returns: < 0 if error, 0 if ok, > 0 for the field_data_pos if
241 // there're more fields to read.
242 //
243 // Fields of the same type that occur consecutively are called
244 // repeating fields.  If a field is processed and there's more "space"
245 // left over, then chances are you are dealing with repeating fields.
246 // To deal with repeating fields, I return the last data position of
247 // the current field.  That way the caller can invoke this function
248 // again with the given data position so that the next field can be
249 // read.
250 
251 static
252 int
fillScField_(sio_8211Field const & dr_field,sio_8211FieldFormat const & field_format,sc_Field & sc_field,long field_data_pos=0)253 fillScField_( sio_8211Field const &       dr_field,
254               sio_8211FieldFormat const & field_format,
255               sc_Field&                   sc_field,
256               long                        field_data_pos = 0 )
257 {
258   vector<char>::const_iterator field_data = dr_field.getData().begin();
259   field_data += field_data_pos;
260 
261   const long   field_data_length = dr_field.getDataLength();
262 
263 
264   sc_field.setMnemonic( field_format.getTag() );
265   sc_field.setName( field_format.getName() );
266 
267   // SDTS attributes treat their subfields differently.
268 
269   bool is_attribute  = (field_format.getTag() == "ATTP" ||
270                         field_format.getTag() == "ATTS" );
271 
272 
273   sc_Field::iterator sc_subfield_itr = sc_field.begin();
274 
275 
276   for ( list<sio_8211SubfieldFormat>::const_iterator subfield_format_itr =
277           field_format.begin();
278         subfield_format_itr != field_format.end();
279         subfield_format_itr++, sc_subfield_itr++ )
280     {
281       // This function takes into consideration that the client may be
282       // reusing a sc_Field container.  If they are, then the iterator
283       // will be pointing to the first subfield; in which case, we do nothing
284       // here and just over-write the subfield that the iterator points at.
285       // On the other hand, this could be a "virgin" sc_Field container --
286       // we'll know that if the iterator is pointing off the end of the container.
287       // In that case, we just insert a new subfield.  But.  We still need
288       // a valid iterator.  So, we do everything in a oner: we insert a new
289       // subfield just before the "end" of the container and take the resulting
290       // iterator and use that for this current iteration. [1]
291 
292       if ( sc_subfield_itr == sc_field.end() )
293         {
294           sc_subfield_itr = sc_field.insert( sc_field.end(), sc_Subfield() );
295         }
296       // set the subfield name (or mnemonic) first
297       if ( is_attribute )
298         {
299           (*sc_subfield_itr).setName( (*subfield_format_itr).getLabel() );
300         }
301       else
302         {
303           (*sc_subfield_itr).setMnemonic( (*subfield_format_itr).getLabel() );
304         }
305 
306       // fill the actual subfield value
307 
308       if ( (*subfield_format_itr).getConverter() )
309         {
310           long chunk_size; // how much data was converted in characters
311 
312           // now call the converter bound to the current subfield type to
313           // convert the raw DR subfield into a proper sc_Subfield
314           switch ( (*subfield_format_itr).getFormat() )
315             {
316             case sio_8211SubfieldFormat::fixed :
317               chunk_size =
318                 (*subfield_format_itr).getConverter()->makeFixedSubfield( *sc_subfield_itr,
319                                                                           field_data,
320                                                                           static_cast<long int>((*subfield_format_itr).getLength()) );
321               break;
322             case sio_8211SubfieldFormat::variable :
323               chunk_size =
324                 (*subfield_format_itr).getConverter()->makeVarSubfield( *sc_subfield_itr,
325                                                                         field_data,
326                                                                         static_cast<long int>(field_data_length - field_data_pos),
327                                                                         (*subfield_format_itr).getDelimiter() );
328               field_data++;             // skip unit terminator
329               field_data_pos++;
330               break;
331             }
332           field_data     += chunk_size; // increment cursor past the data
333           field_data_pos += chunk_size;
334         }
335       else
336         {  // XXX Yes, Virginia, there is such a thing as good error handling.
337 #ifdef DEBUG
338           cerr << (*subfield_format_itr).getLabel()
339                << " had no converter" << endl;
340 #endif
341           return -1;
342         }
343     }
344 
345   // if we've re-used this subfield container, then there may be some
346   // leftover subfields at the end; if so, clean this up
347 
348   if ( sc_subfield_itr != sc_field.end() )
349     {
350       sc_field.erase( sc_subfield_itr, sc_field.end() );
351     }
352 
353   // All fields MUST end with a field terminator, even binary fields.
354   // However, if we're dealing with a _repeating_ field, then only the
355   // last field in the sequence will have a field terminator.
356   // Therefore, we check that there's only one character left in the
357   // current field; if there is only one, then that's likely the field
358   // terminator.  Iff that's the case, increment the data pointer past
359   // it.
360 
361   // Most of the time it's ok to check for a field terminator while
362   // there's still field data to be parsed; however, in the case of
363   // binary data, it's possible by happenstance that the current octet
364   // will correspond to a field terminator, which would then cause the
365   // data pointer to be erroneously incremented.  This is why we first
366   // check to insure that there's only one character left in the field
367   // before checking that it is indeed a field terminator.
368 
369   if ( (1 == field_data_length - field_data_pos )  &&
370        (sio_8211FieldTerminator == *field_data) )
371     {
372       field_data++;             // skip any field terminator
373       field_data_pos++;
374     }
375 
376 
377   // if the field data position is less than the length, then we're likely
378   // dealing with a repeating field; so, return the position so that the
379   // caller can invoke this function again to pick up the next field.
380   // Otherwise, return zero indicating everything is ok.
381 
382   return ( field_data_pos < field_data_length ) ? field_data_pos : 0;
383 
384 } // fillScField_
385 
386 
387 
388 
389 
390 bool
fillScRecord_(sio_8211DR const & dr,sc_Record & sc_record)391 sio_8211Reader::fillScRecord_( sio_8211DR const & dr, sc_Record& sc_record )
392 {
393 
394   sio_8211Directory const& dr_directory = dr.getDirectory();
395 
396 
397   list<sio_8211FieldFormat>::const_iterator field_format_itr;
398 
399 
400   list<sc_Field>::iterator sc_field_itr = sc_record.begin();
401 
402 
403   long field_data_pos = 0;
404 
405   // enumerate each DR field, converting and adding them
406   // to the sc_record
407 
408   for ( sio_8211Directory::const_iterator dir_entry = dr_directory.begin();
409         dir_entry != dr_directory.end();
410         dir_entry++ )
411     {
412       // find the field format
413       field_format_itr = find( imp_->fieldFormats_.begin(), imp_->fieldFormats_.end(),
414                                (*dir_entry).getTag() );
415 
416       if ( field_format_itr != imp_->fieldFormats_.end() )
417         {
418           sio_8211Field const * dr_field = (*dir_entry).getField();
419 
420           do
421             {
422                                 // if we're not reusing an existing record
423                                 // and it's empty, then add the first field
424                                 // (see [1], above -- the same kind of thing
425                                 // is going on here)
426 
427               if ( sc_field_itr == sc_record.end() )
428                 {
429                   sc_field_itr = sc_record.insert( sc_record.end(), sc_Field() );
430                 }
431 
432 
433               field_data_pos = fillScField_( *dr_field,
434                                              (*field_format_itr),
435                                              (*sc_field_itr),
436                                              field_data_pos );
437 
438               if ( field_data_pos < 0 )
439               {
440                  return false;
441               }
442 
443               sc_field_itr++;
444 
445             }
446           while ( field_data_pos > 0 ); // while we've got repeating fields
447         }
448       else
449         {                       // XXX Error handling, anyone?  (But for
450                                 // XXX reserved tags, this is ok.)
451 #ifdef DEBUG
452           cerr << "field format for "
453                << (*dir_entry).getTag() << " not found" << endl;
454 #endif
455         }
456 
457 
458     }
459 
460   // snip off any fields that may be left over
461   // from the last time the record was used
462 
463   if ( sc_field_itr != sc_record.end() )
464     {
465       sc_record.erase( sc_field_itr, sc_record.end() );
466     }
467 
468   return true;
469 
470 } // sio_8211Reader::_fillScRecord()
471 
472 
473 
474 
475 bool
fillScRecord_(long DRoffset,sc_Record & sc_record)476 sio_8211Reader::fillScRecord_( long DRoffset, sc_Record& sc_record )
477 {
478   sio_8211DR dr;
479 
480   imp_->file_.seekg( DRoffset );
481 
482   imp_->file_ >> dr;
483 
484   return fillScRecord_( dr, sc_record );
485 
486 } // sio_8211Reader::_fillScRecord()
487 
488 
489 
490 
sio_8211Reader()491 sio_8211Reader::sio_8211Reader()
492   : imp_( 0x0 )                 // initially start out with a no implementation
493 {                               // this can be set later by attach()
494 } // sio_8211Reader ctor
495 
496 
497 
sio_8211Reader(istream & is,sio_8211_converter_dictionary const * const converters)498 sio_8211Reader::sio_8211Reader( istream & is,
499                                 sio_8211_converter_dictionary const * const converters )
500   : imp_(  new sio_8211Reader_Imp( is )  )
501 {
502   if ( imp_ )
503     {
504       readDDR_( *imp_, converters );
505     }
506   // XXX else we have some problems here
507 } // sio_8211Reader ctor
508 
509 
510 
511 
512 
513 //
514 // Iterator stuff
515 //
516 
517 
518 
519 
520 struct
521 sio_8211ForwardIteratorImp
522 {
523 
524   sio_8211ForwardIteratorImp( );
525 
526   sio_8211ForwardIteratorImp( sio_8211ForwardIteratorImp const & );
527 
528   sio_8211ForwardIteratorImp& operator=( sio_8211ForwardIteratorImp const & );
529 
530   sio_8211ForwardIteratorImp( sio_8211Reader & reader );
531 
532   bool attach( sio_8211Reader & reader ); // dock with given reader
533 
534 
535   sio_8211Reader* reader_;      // reader iterator currently docked with
536 
537   bool isDone_;                 // true iff the iterator is pointing of the
538 				// DA's "end"  XXX seems kludgy, but makes
539 				// iterator done() more intuitive and fixes
540 				// premature finishing behavior.
541 
542   long DR_end_;                 // stream positioned just past current
543 				// DR end
544 
545   sio_8211DR DR_;
546 
547 }; // sio_8211ForwardIteratorImp
548 
549 
550 
551 
552 
553 
sio_8211ForwardIteratorImp()554 sio_8211ForwardIteratorImp::sio_8211ForwardIteratorImp( )
555   : reader_( 0x0 ), isDone_( false ), DR_end_( 0 )
556 {
557 } // sio_8211ForwardIteratorImp::ctor
558 
559 
560 
sio_8211ForwardIteratorImp(sio_8211ForwardIteratorImp const & rhs)561 sio_8211ForwardIteratorImp::sio_8211ForwardIteratorImp( sio_8211ForwardIteratorImp const & rhs )
562   : reader_( rhs.reader_ ),
563     isDone_( rhs.isDone_ ),
564     DR_end_( rhs.DR_end_ ),
565     DR_( rhs.DR_ )
566 {
567 } // sio_8211ForwardIteratorImp::ctor
568 
569 
570 
571 
572 sio_8211ForwardIteratorImp&
operator =(sio_8211ForwardIteratorImp const & rhs)573 sio_8211ForwardIteratorImp::operator=( sio_8211ForwardIteratorImp const & rhs )
574 {
575     if ( this == &rhs ) return *this;
576 
577     reader_ = rhs.reader_;
578     isDone_ = rhs.isDone_;
579     DR_end_ = rhs.DR_end_;
580     DR_     = rhs.DR_;
581 
582     return *this;
583 } // sio_8211ForwardIteratorImp::ctor
584 
585 
586 
587 
sio_8211ForwardIteratorImp(sio_8211Reader & reader)588 sio_8211ForwardIteratorImp::sio_8211ForwardIteratorImp( sio_8211Reader & reader )
589   : reader_( 0x0 ), isDone_( false ), DR_end_( 0 )
590 {
591   attach( reader );             // dock with the given reader
592 } // sio_8211ForwardIteratorImp::ctor
593 
594 
595 
596 
597 bool
attach(sio_8211Reader & reader)598 sio_8211ForwardIteratorImp::attach( sio_8211Reader & reader )
599 {
600   reader_ = &reader;
601 
602                                 // first insure that the reader is at
603                                 // the first DR
604 
605   reader_->imp_->file_.seekg( reader_->imp_->DRStart_ );
606 
607   reader_->imp_->file_.peek();  // FORCE stream to set fail bit if seek
608                                 // beyond end
609 
610 
611                                 // if the stream is in an ok state,
612                                 // read in the first DR.
613 
614   if ( reader_->imp_->file_.good() )
615     {
616                                 // then snarf the first DR
617       reader_->imp_->file_ >> DR_;
618 
619                                 // remember where we are because we'll
620                                 // need that information
621                                 // for operator++()
622 
623       DR_end_ = reader_->imp_->file_.tellg();
624 
625                                 // check to see if we're dealing with
626                                 // dropped leaders
627 
628       if ( ! DR_.isReusingLeaderAndDirectory() &&
629            'R' == DR_.getLeader().getLeaderIdentifier() )
630         {
631           DR_.reuseLeaderAndDirectory( true );
632         }
633     }
634   else // in the unlikely event that there are NO DR's ...
635     {
636       isDone_ = true;
637     }
638 
639 
640   return reader_->imp_->file_.good();
641 
642 } // sio_8211ForwardIteratorImp::attach
643 
644 
645 
646 
647 //
648 // sio_8211ForwardIterator
649 //
650 
651 
652 
sio_8211ForwardIterator()653 sio_8211ForwardIterator::sio_8211ForwardIterator()
654   : imp_( new sio_8211ForwardIteratorImp() )
655 {
656 } // sio_8211ForwardIterator ctor
657 
658 
659 
sio_8211ForwardIterator(sio_8211Reader & reader)660 sio_8211ForwardIterator::sio_8211ForwardIterator( sio_8211Reader & reader )
661   : imp_( new sio_8211ForwardIteratorImp( reader ) )
662 {
663 } // sio_8211ForwardIterator ctor
664 
665 
666 
sio_8211ForwardIterator(sio_8211ForwardIterator const & fi)667 sio_8211ForwardIterator::sio_8211ForwardIterator( sio_8211ForwardIterator const & fi )
668   : imp_( new sio_8211ForwardIteratorImp( *fi.imp_ ) )
669 {
670 } // sio_8211ForwardIterator ctor
671 
672 
673 
674 sio_8211ForwardIterator&
operator =(sio_8211ForwardIterator const & rhs)675 sio_8211ForwardIterator::operator=( sio_8211ForwardIterator const & rhs )
676 {
677   if ( this == &rhs ) return *this;
678 
679   *imp_ = *rhs.imp_;
680 
681   return *this;
682 } // sio_8211ForwardIterator ctor
683 
684 
685 
~sio_8211ForwardIterator()686 sio_8211ForwardIterator::~sio_8211ForwardIterator()
687 {
688   if ( imp_ ) { delete imp_; }
689 } // sio_8211ForwardIterator dtor
690 
691 
692 
693 
694 bool
get(sc_Record & record)695 sio_8211ForwardIterator::get( sc_Record& record )
696 {
697   // Set the bad bit if the record read failed -- this could have
698   // happened because the proper converter couldn't be found,
699   // there was no field terminator for one of 8211 records, or some
700   // more mundane I/O related problem.
701   if ( ! imp_->reader_->fillScRecord_( imp_->DR_, record ) )
702     {
703       imp_->reader_->imp_->file_.setstate( ios::badbit );
704       return false;
705     }
706   return true;
707 } // sio_8211ForwardIterator::get
708 
709 
710 
711 void
operator ++()712 sio_8211ForwardIterator::operator++()
713 {
714 
715   // first seek to where we supposedly left off with the last read as another
716   // iterator may have moved the stream
717 
718   imp_->reader_->imp_->file_.seekg( imp_->DR_end_, ios::beg );
719   imp_->reader_->imp_->file_.peek(); // FORCE stream to set fail bit if seek beyond end
720 
721   // if the stream is in an ok state, read in the next DR.
722 
723   if ( imp_->reader_->imp_->file_.good() )
724     {
725       imp_->reader_->imp_->file_ >> imp_->DR_;
726       imp_->DR_end_ = imp_->reader_->imp_->file_.tellg(); // remember
727                                 // for next ++
728 
729       // check to see if we're dealing with dropped leaders
730 
731       if ( ! imp_->DR_.isReusingLeaderAndDirectory() &&
732            'R' == imp_->DR_.getLeader().getLeaderIdentifier() )
733         {
734           imp_->DR_.reuseLeaderAndDirectory( true );
735         }
736 
737     }
738   else  // whoopsies ... no such record, so flag that we're done
739     {
740       imp_->isDone_ = true;
741     }
742 
743 } // sio_8211ForwardIterator::get
744 
745 
746 
747 
748 bool
done() const749 sio_8211ForwardIterator::done() const
750 {
751   return imp_->isDone_;
752 } // sio_8211ForwardIterator::done
753 
754 
755 
756 
operator void*() const757 sio_8211ForwardIterator::operator void*() const
758 {
759   return ( done() ) ?  reinterpret_cast<void*>(0x0) :
760     reinterpret_cast<void*>(0x1);
761 } // sio_8211ForwardIterator::done
762