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