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