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 
14 #include "sb_Accessor.h"
15 
16 
17 #include <utility>
18 #include <algorithm>
19 #include <functional>
20 
21 #include <iostream>
22 #include <fstream>
23 
24 #include <string>
25 
26 #include <cctype>
27 #include <cstring>
28 
29 #ifdef __FreeBSD__
30 #  include <osreldate.h>
31 #  if __FreeBSD_version < 500035
32 #    include <stlport/iosfwd>
33 #  endif
34 #endif
35 namespace std { using ::mbstate_t; }
36 #include <boost/smart_ptr.hpp>
37 #include <boost/filesystem/path.hpp>
38 #include <boost/filesystem/operations.hpp>
39 #include <boost/filesystem/fstream.hpp>
40 
41 
42 #ifndef INCLUDED_SC_RECORD_H
43 #include <sdts++/container/sc_Record.h>
44 #endif
45 
46 #ifndef INCLUDED_SIO_READER_H
47 #include <sdts++/io/sio_Reader.h>
48 #endif
49 
50 #ifndef INCLUDED_SB_CATD_H
51 #include <sdts++/builder/sb_Catd.h>
52 #endif
53 
54 
55 using namespace std;
56 
57 
58 static const char* ident_ =
59    "$Id: sb_Accessor.cpp,v 1.25 2003/06/10 20:51:40 mcoletti Exp $";
60 
61 
62 
63 
64 
65 //
66 // The accessor will keep a map of these things keyed by module type.
67 // sb_Accessor::catd() is used to populate the map; after that
68 // function is called each module found in the CATD module will have a
69 // corresponding ``module''.  The only thing set will be the file
70 // name.  The stream should be null; the reader and iterator won't be
71 // set to anything meaningful.  Only when the user requests a specific
72 // module do these things get populated.  So the first invocation of
73 // sb_Accessor::get() will create a stream and open it to the given
74 // ``file_name''.  It will then attach the ``reader'' to the stream
75 // which, in turn, will have ``curr_record'' set to the first record.
76 // The stream is deleted only when the encapsulating accessor is as we
77 // us the stream's null-ness to determine whether we've already tried
78 // to read it or not.
79 //
80 
81 struct moduleDescriptor
82 {
83   boost::shared_ptr<boost::filesystem::ifstream> stream;    // stream pointing to an SDTS module file
84                                 // boost::shared_ptr to keep a canonical copy once all
85                                 // the dust settles from the ctors and copy
86                                 // ctors that the STL map::insert() is gonna do
87 
88   boost::filesystem::path       file_path; // the corresponding file path for the given
89                                 // stream;  to be used to open the stream
90                                 // only if the user wants to access the module
91 
92 
93                                 // its corresponding reader;
94                                 // boost::shared_ptr<> insures that we've got a
95                                 // canonical copy; also we have to
96                                 // have dynamically allocate a reader
97                                 // as it doesn't support a copy ctor,
98                                 // and so can't be inserted into a STL
99                                 // container
100   boost::shared_ptr<sio_8211Reader> reader;
101 
102 
103   sio_8211ForwardIterator curr_record;
104 
moduleDescriptormoduleDescriptor105   moduleDescriptor() {}
106 
moduleDescriptormoduleDescriptor107   moduleDescriptor( moduleDescriptor const& rhs)
108     : stream( rhs.stream ),
109       file_path( rhs.file_path ),
110       reader( rhs.reader ),
111       curr_record( rhs.curr_record )
112     {}
113 
114 }; // struct moduleDescriptor
115 
116 
117 
118 struct sb_Accessor_Imp
119 {                               // dictionary of module contexts keyed by
120                                 // their respective module type
121 
122       map<string,moduleDescriptor> modules;
123 
124       /// CATD file name
125       std::string fileName;
126 
127 
sb_Accessor_Impsb_Accessor_Imp128       sb_Accessor_Imp( )
129       {}
130 
sb_Accessor_Impsb_Accessor_Imp131       sb_Accessor_Imp( std::string const & fn )
132          : fileName( fn )
133       {}
134 
135 }; // struct sb_Accessor_imp
136 
137 
138 
139 
sb_Accessor()140 sb_Accessor::sb_Accessor( )
141   : imp_( new sb_Accessor_Imp )
142 {
143 
144 } // sb_Accessor ctor
145 
146 
147 
148 
sb_Accessor(string const & catd_fn)149 sb_Accessor::sb_Accessor( string const& catd_fn )
150   : imp_( new sb_Accessor_Imp( catd_fn ) )
151 {
152   readCatd( catd_fn );
153 } // sb_Accessor ctor
154 
155 
156 
157 
~sb_Accessor()158 sb_Accessor::~sb_Accessor( )
159 {
160   delete imp_;
161 } // sb_Accessor dtor
162 
163 
164 
165 static const char* module_mnemonics_[] =
166 {
167   "CATS",
168   "CATD",
169   "DDOM",
170   "DDSH",
171   "MDOM",
172   "MDEF",
173   "DQHL",
174   "DQPA",
175   "DQAA",
176   "DQLC",
177   "DQCG",
178   "IDEN",
179   "IREF",
180   "LDEF",
181   "RSDF",
182   "STAT",
183   "XREF",
184   ""
185 }; // module_mnemonics
186 
187 
188 
189 // returns true if the mnemonic corresponds to a valid SDTS module mnemonic
190 //
191 inline
192 static
193 bool
_isValid(string const & mnemonic)194 _isValid( string const& mnemonic )
195 {
196    int i(0);
197 
198    while ( "" != module_mnemonics_[i] )
199       {
200          if ( mnemonic == module_mnemonics_[i] )
201             {
202                return true;
203             }
204          ++i;
205       }
206    return false;
207 } // isValid_
208 
209 
210 
211 
212 
213 
214 // later used in a STL transform() to upcase a string
215 //
216 inline
217 char
toupper_(char c)218 toupper_( char c )
219 {
220     return toupper( c );
221 } // toupper_
222 
223 
224 
225 
226 bool
readCatd(std::string const & catd_fn)227 sb_Accessor::readCatd( std::string const& catd_fn )
228 {
229   imp_->fileName = catd_fn;
230 
231   boost::filesystem::ifstream catd_stream( catd_fn );
232 
233                                 // bail if we can't open the CATD
234                                 // module
235 
236   if ( ! catd_stream ) { return false; }
237 
238 
239   imp_->modules.clear();        // blow away any old entries
240 
241 
242   sio_8211Reader          reader( catd_stream );
243   sio_8211ForwardIterator curr_record( reader );
244 
245                                 // bail if the reader's input stream
246                                 // is wedged before we're even ready
247                                 // to read
248   if ( ! catd_stream )
249   {
250      return false;
251   }
252                                 // similarly, something is wrong if
253                                 // the iterator starts out empty
254   if ( ! curr_record )
255   {
256      return false;
257   }
258 
259   sc_Record catd_record;
260 
261   sb_Catd   catd_module;
262                                 // assume that all the
263                                 // modules we're going to look at are
264                                 // in the same directory as the CATD
265                                 // module; so, strip out the directory
266                                 // part of the path name so we can
267                                 // later prepend that to the file
268                                 // names we find in the CATD module
269 
270   boost::filesystem::path catd_path( catd_fn );
271   catd_path = catd_path.branch_path();
272 
273 
274                                 // grind through the CATD record,
275                                 // appending module records for each
276                                 // entry
277 
278   string      module_name;
279   string      file_name;
280 
281   moduleDescriptor dummy_descriptor; // a starter module descriptor
282                                 // that is assigned to new module entries
283 
284   while ( curr_record )
285     {
286                                 // fetch the current CATD record and pull out
287                                 // the file name
288 
289       if ( ! curr_record.get( catd_record ) ) { break; }
290 
291       if ( ! catd_module.setRecord( catd_record ) )
292         {
293           return false;         // not a valid CATD module
294         }
295 
296       if ( ! catd_module.getNAME( module_name ) ) { return false; }
297 
298 
299                                 // insure that the module mnemonic is
300                                 // in all upper case; mind that it
301                                 // _should_ already be this way, but
302                                 // it doesn't hurt to make sure
303 
304       transform( module_name.begin(), module_name.end(),
305                  module_name.begin(), toupper_ );
306 
307 
308 
309       pair< map<string,moduleDescriptor >::iterator, bool > p =
310         imp_->modules.insert( make_pair(module_name, moduleDescriptor() ) );
311 
312                                 // the insertion will have failed if
313                                 // the module already has an entry;
314                                 // XXX add support later for multiple
315                                 // XXX modules
316       if ( ! p.second )
317         {
318            ++curr_record;
319            continue;
320         }
321 
322                                 // stick the path back on and store it
323                                 // with the module record
324 
325       if ( ! catd_module.getFILE( file_name ) ) { return false; }
326 
327       (*p.first).second.file_path = catd_path / file_name;
328 
329 
330       ++curr_record;            // go on to next CATD record entry
331 
332     }
333 
334   return true;
335 } //sb_Accessor::readCatd
336 
337 
338 
339 
340 
341 // This returns an iterator to a record for the module that has the
342 // given mnemonic.  Return false if there are either no more records
343 // or a module of that corresponds to the given mnemonic doesn't
344 // exist.
345 static
346 bool
getModuleIterator_(sb_Accessor_Imp & accessor,string const & module_mnemonic,sio_8211ForwardIterator & fi,sio_8211_converter_dictionary * cv)347 getModuleIterator_( sb_Accessor_Imp & accessor,
348                     string const& module_mnemonic,
349                     sio_8211ForwardIterator & fi,
350                     sio_8211_converter_dictionary* cv )
351 {
352                                 // first see if we already have a
353                                 // module record for the given
354                                 // mnemnonic; if not, then we create one
355 
356   map<string,moduleDescriptor>::iterator module_itr =
357     accessor.modules.find( module_mnemonic );
358 
359   if ( module_itr == accessor.modules.end() ) // *bzzt* Not there.  Bail.
360     {
361        // Actually, if we're dealing with a DDSH or DDOM module, then
362        // there's a chance that these are _master_ DDSH or DDOM
363        // modules, which use different mnemonics.  DDSH becomes MDEF
364        // and DDOM becomes MDOM.  So if the current module_mnemonic is
365        // either of those, make the appropriate correction and try
366        // again.
367        // XXX probably should add checks for the other alternative names
368        // XXX mentioned in the TVP spec.
369 
370        string tmp_string;
371 
372        if ( "DDSH" == module_mnemonic )
373        {
374           tmp_string = "MDEF";
375           module_itr =
376              accessor.modules.find( tmp_string );
377 
378           if ( module_itr == accessor.modules.end() )
379           {
380              // ok, now we REALLY give up
381              return false;
382           }
383        }
384        else if ( "DDOM" == module_mnemonic )
385        {
386           tmp_string = "MDOM";
387           module_itr =
388              accessor.modules.find( tmp_string );
389 
390           if ( module_itr == accessor.modules.end() )
391           {
392              // ok, now we REALLY give up
393              return false;
394           }
395        }
396        else
397        {
398           return false;
399        }
400     }
401 
402                                 // if the stream isn't open, make it so!
403 
404   if ( ! module_itr->second.stream.get() )
405     {
406       module_itr->second.stream =
407          boost::shared_ptr<boost::filesystem::ifstream>( new boost::filesystem::ifstream( module_itr->second.file_path,
408                                                                                           std::ios::in )  );
409 
410                                 // bail if we fail to either get a new
411                                 // stream or the new stream itself is
412                                 // already wedged
413 
414       if ( ! ((*module_itr).second.stream.get() &&
415               (*module_itr).second.stream->good() ) )
416         {
417           return false;
418         }
419                                 // now let's insure that there's a
420                                 // reader; if not, make a new one
421 
422       if ( ! module_itr->second.reader.get() )
423         {
424           module_itr->second.reader =
425             boost::shared_ptr<sio_8211Reader>( new sio_8211Reader( *module_itr->second.stream, cv ) );
426         }
427       else                      // we already have a reader, so just attach
428         {                       // it to the stream
429 
430           module_itr->second.reader->attach( *module_itr->second.stream, cv );
431         }
432 
433                                 // attach the iterator, in turn, to
434                                 // the reader; return since the
435                                 // iterator will be pointing to the
436                                 // first record in the module
437 
438       fi = module_itr->second.curr_record =
439         (*module_itr).second.reader->begin();
440 
441       return true;
442     }
443                                 // if we get here, then we've already
444                                 // read at least one record; if there
445                                 // are no more records, close the
446                                 // associated stream and return false
447 
448   if ( (*module_itr).second.curr_record.done() )
449     {
450       (*module_itr).second.stream->close();
451       return false;
452     }
453                                 // increment to the next record, if any
454                                 // exist
455 
456   ++(*module_itr).second.curr_record;
457 
458   fi = (*module_itr).second.curr_record;
459 
460   return true;
461 
462 } // getModuleIterator_
463 
464 
465 
466 
467 bool
get(sb_Module & module,sio_8211_converter_dictionary * cv)468 sb_Accessor::get( sb_Module& module, sio_8211_converter_dictionary* cv )
469 {
470                                 // first we find the iterator for the
471                                 // given module
472 
473   sio_8211ForwardIterator curr_record_itr;
474 
475   if ( ! getModuleIterator_( *imp_, module.getMnemonic(),
476                              curr_record_itr,
477                              cv )  )
478     {
479       return false;
480     }                           // now interpret the content of the
481                                 // current record by passing it to the
482                                 // module; if the record is bogus in
483                                 // some way, return false (e.g., an
484                                 // IDEN record is passed to a sb_Catd
485                                 // object
486 
487   sc_Record curr_record;
488 
489   if ( curr_record_itr.done() ||
490        (! curr_record_itr.get( curr_record )) )
491     {
492       return false;
493     }
494 
495   return module.setRecord( curr_record );
496 
497 } // sb_Accessor::get
498 
499 
500 
501 
502 std::string const &
fileName() const503 sb_Accessor::fileName() const
504 {
505    return imp_->fileName;
506 } // sb_Accessor::fileName() const
507