1 // vil_nitf2: Written by Harry Voorhees (hlv@) and Rob Radtke (rob@) of
2 // Stellar Science Ltd. Co. (stellarscience.com) for
3 // Air Force Research Laboratory, 2005.
4 
5 #include "vil_nitf2_tagged_record.h"
6 #include <iomanip>
7 #include <iostream>
8 #include <sstream>
9 #include <string>
10 #include <utility>
11 
12 #ifdef _MSC_VER
13 #  include "vcl_msvc_warnings.h"
14 #endif
15 
16 #include "vil/vil_stream_core.h"
17 #include "vil/vil_stream_section.h"
18 
19 #include "vil_nitf2_tagged_record_definition.h"
20 #include "vil_nitf2_field_definition.h"
21 #include "vil_nitf2_field_sequence.h"
22 #include "vil_nitf2_index_vector.h"
23 #include "vil_nitf2_typed_field_formatter.h"
24 #include "vil_nitf2_scalar_field.h"
25 
26 vil_nitf2_field_definition &
s_length_definition()27 vil_nitf2_tagged_record::s_length_definition()
28 {
29   static vil_nitf2_field_definition length_definition("CEL", "Extension Length", new vil_nitf2_integer_formatter(5));
30   return length_definition;
31 }
32 
33 vil_nitf2_field_definition &
s_tag_definition()34 vil_nitf2_tagged_record::s_tag_definition()
35 {
36   static vil_nitf2_field_definition tag_definition("CETAG", "Extension Tag", new vil_nitf2_string_formatter(6));
37   return tag_definition;
38 }
39 
40 vil_nitf2_integer_formatter &
s_length_formatter()41 vil_nitf2_tagged_record::s_length_formatter()
42 {
43   static vil_nitf2_integer_formatter length_formatter(5);
44   return length_formatter;
45 }
46 
47 vil_nitf2_string_formatter &
s_tag_formatter()48 vil_nitf2_tagged_record::s_tag_formatter()
49 {
50   static vil_nitf2_string_formatter tag_formatter(6);
51   return tag_formatter;
52 }
53 
54 std::string
name() const55 vil_nitf2_tagged_record::name() const
56 {
57   std::string cetag;
58   if (m_tag_field->value(cetag))
59     return cetag;
60   else
61     return "<Unknown>";
62 }
63 
64 std::string
pretty_name() const65 vil_nitf2_tagged_record::pretty_name() const
66 {
67   if (m_definition)
68     return m_definition->m_pretty_name;
69   else
70     return "<unknown>";
71 }
72 
73 vil_nitf2_tagged_record *
create(vil_nitf2_istream & input)74 vil_nitf2_tagged_record::create(vil_nitf2_istream & input)
75 {
76   auto * record = new vil_nitf2_tagged_record();
77   if (record->read(input))
78   {
79     return record;
80   }
81   else
82   {
83     delete record;
84     return nullptr;
85   }
86 }
87 
88 bool
read(vil_nitf2_istream & input)89 vil_nitf2_tagged_record::read(vil_nitf2_istream & input)
90 {
91   // Read TRE tag
92   m_tag_field = vil_nitf2_scalar_field::read(input, &s_tag_definition());
93   if (!m_tag_field)
94   {
95     std::cerr << "Error reading extension tag at offset " << input.tell() << ".\n";
96     // Can't continue reading file
97     return false;
98   }
99   std::string cetag;
100   m_tag_field->value(cetag);
101 
102   // Read TRE data length
103   m_length_field = vil_nitf2_scalar_field::read(input, &s_length_definition());
104   if (!m_length_field)
105   {
106     std::cerr << "Error reading extension length for tag " << cetag << ".\n";
107     // Can't continue reading file
108     return false;
109   }
110   m_length_field->value(m_length);
111 
112   // See if this record is defined ...
113   vil_nitf2_tagged_record_definition * record_definition = vil_nitf2_tagged_record_definition::find(cetag);
114 
115   // ... if not, skip ahead to next record ...
116   if (record_definition == nullptr)
117   {
118     VIL_NITF2_LOG(log_info) << "Skipping unknown record " << cetag << ".\n";
119     // Return whether I've found end of record
120     // input.seekg(ceLength, std::ios::cur);
121     input.seek(input.tell() + m_length);
122     return input.ok();
123   }
124   // ... otherwise, populate this record's fields.
125   // First save the position to check later that we read entire record
126   vil_streampos record_data_start_pos = input.tell();
127   m_definition = record_definition;
128   m_field_sequence = new vil_nitf2_field_sequence(record_definition->field_definitions());
129   m_field_sequence->read(input);
130 
131   // Check that the expected amount of data was read
132   vil_streampos expected_pos = record_data_start_pos;
133   expected_pos += m_length;
134   if (input.tell() != expected_pos)
135   {
136     std::cerr << "vil_nitf2_tagged_record::read(): Read " << input.tell() - record_data_start_pos
137               << " bytes instead of " << m_length << " as expected in " << cetag << ".\n";
138     // Attempt to reposition input stream to end of record; return whether
139     // successful
140     input.seek(expected_pos);
141     return input.ok();
142   }
143   // At end of record, as expected
144   return true;
145 }
146 
147 bool
get_value(std::string tag,int & out_value) const148 vil_nitf2_tagged_record::get_value(std::string tag, int & out_value) const
149 {
150   return m_field_sequence->get_value(std::move(tag), out_value);
151 }
152 
153 bool
get_value(std::string tag,double & out_value) const154 vil_nitf2_tagged_record::get_value(std::string tag, double & out_value) const
155 {
156   return m_field_sequence->get_value(std::move(tag), out_value);
157 }
158 
159 bool
get_value(std::string tag,char & out_value) const160 vil_nitf2_tagged_record::get_value(std::string tag, char & out_value) const
161 {
162   return m_field_sequence->get_value(std::move(tag), out_value);
163 }
164 bool
get_value(std::string tag,void * & out_value) const165 vil_nitf2_tagged_record::get_value(std::string tag, void *& out_value) const
166 {
167   return m_field_sequence->get_value(std::move(tag), out_value);
168 }
169 
170 bool
get_value(std::string tag,std::string & out_value) const171 vil_nitf2_tagged_record::get_value(std::string tag, std::string & out_value) const
172 {
173   return m_field_sequence->get_value(std::move(tag), out_value);
174 }
175 
176 bool
get_value(std::string tag,vil_nitf2_location * & out_value) const177 vil_nitf2_tagged_record::get_value(std::string tag, vil_nitf2_location *& out_value) const
178 {
179   return m_field_sequence->get_value(std::move(tag), out_value);
180 }
181 
182 bool
get_value(std::string tag,vil_nitf2_date_time & out_value) const183 vil_nitf2_tagged_record::get_value(std::string tag, vil_nitf2_date_time & out_value) const
184 {
185   return m_field_sequence->get_value(std::move(tag), out_value);
186 }
187 
188 #if VXL_HAS_INT_64
189 // if not VXL_HAS_INT_64 isn't defined the vil_nitf2_long is the same as just plain 'int'
190 // and this function will be a duplicate of that get_value
191 bool
get_value(std::string tag,vil_nitf2_long & out_value) const192 vil_nitf2_tagged_record::get_value(std::string tag, vil_nitf2_long & out_value) const
193 {
194   return m_field_sequence->get_value(std::move(tag), out_value);
195 }
196 #endif
197 
198 bool
get_value(std::string tag,const vil_nitf2_index_vector & indexes,int & out_value) const199 vil_nitf2_tagged_record::get_value(std::string tag, const vil_nitf2_index_vector & indexes, int & out_value) const
200 {
201   return m_field_sequence->get_value(std::move(tag), indexes, out_value);
202 }
203 
204 bool
get_value(std::string tag,const vil_nitf2_index_vector & indexes,double & out_value) const205 vil_nitf2_tagged_record::get_value(std::string tag, const vil_nitf2_index_vector & indexes, double & out_value) const
206 {
207   return m_field_sequence->get_value(std::move(tag), indexes, out_value);
208 }
209 
210 bool
get_value(std::string tag,const vil_nitf2_index_vector & indexes,char & out_value) const211 vil_nitf2_tagged_record::get_value(std::string tag, const vil_nitf2_index_vector & indexes, char & out_value) const
212 {
213   return m_field_sequence->get_value(std::move(tag), indexes, out_value);
214 }
215 
216 bool
get_value(std::string tag,const vil_nitf2_index_vector & indexes,void * & out_value) const217 vil_nitf2_tagged_record::get_value(std::string tag, const vil_nitf2_index_vector & indexes, void *& out_value) const
218 {
219   return m_field_sequence->get_value(std::move(tag), indexes, out_value);
220 }
221 
222 bool
get_value(std::string tag,const vil_nitf2_index_vector & indexes,std::string & out_value) const223 vil_nitf2_tagged_record::get_value(std::string tag,
224                                    const vil_nitf2_index_vector & indexes,
225                                    std::string & out_value) const
226 {
227   return m_field_sequence->get_value(std::move(tag), indexes, out_value);
228 }
229 
230 bool
get_value(std::string tag,const vil_nitf2_index_vector & indexes,vil_nitf2_location * & out_value) const231 vil_nitf2_tagged_record::get_value(std::string tag,
232                                    const vil_nitf2_index_vector & indexes,
233                                    vil_nitf2_location *& out_value) const
234 {
235   return m_field_sequence->get_value(std::move(tag), indexes, out_value);
236 }
237 
238 bool
get_value(std::string tag,const vil_nitf2_index_vector & indexes,vil_nitf2_date_time & out_value) const239 vil_nitf2_tagged_record::get_value(std::string tag,
240                                    const vil_nitf2_index_vector & indexes,
241                                    vil_nitf2_date_time & out_value) const
242 {
243   return m_field_sequence->get_value(std::move(tag), indexes, out_value);
244 }
245 
246 #if VXL_HAS_INT_64
247 // if not VXL_HAS_INT_64 isn't defined the vil_nitf2_long is the same as just plain 'int'
248 // and this function will be a duplicate of that get_value
249 bool
get_value(std::string tag,const vil_nitf2_index_vector & indexes,vil_nitf2_long & out_value) const250 vil_nitf2_tagged_record::get_value(std::string tag,
251                                    const vil_nitf2_index_vector & indexes,
252                                    vil_nitf2_long & out_value) const
253 {
254   return m_field_sequence->get_value(std::move(tag), indexes, out_value);
255 }
256 #endif
257 
258 // Macro to define both overloads of get_values()
259 #define VIL_NITF2_TAGGED_RECORD_GET_VALUES(T)                                                                          \
260   bool vil_nitf2_tagged_record::get_values(                                                                            \
261     std::string tag, const vil_nitf2_index_vector & indexes, std::vector<T> & out_values, bool clear_out_values) const \
262   {                                                                                                                    \
263     return m_field_sequence->get_values(tag, indexes, out_values, clear_out_values);                                   \
264   }                                                                                                                    \
265   bool vil_nitf2_tagged_record::get_values(std::string tag, std::vector<T> & out_values) const                         \
266   {                                                                                                                    \
267     return m_field_sequence->get_values(tag, out_values);                                                              \
268   }
269 
270 VIL_NITF2_TAGGED_RECORD_GET_VALUES(int);
271 VIL_NITF2_TAGGED_RECORD_GET_VALUES(double);
272 VIL_NITF2_TAGGED_RECORD_GET_VALUES(char);
273 VIL_NITF2_TAGGED_RECORD_GET_VALUES(void *);
274 VIL_NITF2_TAGGED_RECORD_GET_VALUES(std::string);
275 VIL_NITF2_TAGGED_RECORD_GET_VALUES(vil_nitf2_location *);
276 VIL_NITF2_TAGGED_RECORD_GET_VALUES(vil_nitf2_date_time);
277 #if VXL_HAS_INT_64
278 VIL_NITF2_TAGGED_RECORD_GET_VALUES(vil_nitf2_long);
279 #endif
280 
281 vil_nitf2_tagged_record::vil_nitf2_tagged_record()
282 
283     = default;
284 
285 // TO DO: rewrite this method a sequence of unit tests!
286 //
287 bool
test()288 vil_nitf2_tagged_record::test()
289 {
290   bool error = false;
291   const char * test_tre_tag = "@TEST@";
292   // Example Tagged Record Extension definition
293   vil_nitf2_tagged_record_definition::define(test_tre_tag, "Test Definition")
294     .field("MTI_DP", "Destination Point", NITF_INT(2))
295     .field("MTI_PACKET_ID", "MTI Packed ID Number", NITF_INT(3))
296     .field("DATIME", "Scan Date & Time", NITF_DAT(14), true)
297     .field("ACFT_LOC", "Aircraft Position", NITF_LOC(21))
298     .field("ACFT_LOC2", "Aircraft Position 2", NITF_LOC(21))
299     .field("SQUINT_ANGLE", "Squint Angle", NITF_DBL(6, 2, true), true)
300     .field("NO_VALID_TARGETS", "Number of Targets", NITF_INT(3))
301     .field("TGT_CAT",
302            "Target Classification Category",
303            NITF_ENUM(1,
304                      vil_nitf2_enum_values()
305                        .value("H", "Helicopter")
306                        .value("T", "Tracked")
307                        .value("U", "Unknown")
308                        .value("W", "Wheeled")
309                        .value("TOO LONG", "Too long value test")
310                        .value("T", "Duplicate value test")))
311     .repeat(new vil_nitf2_field_value<int>("NO_VALID_TARGETS"),
312             vil_nitf2_field_definitions()
313               .field("TGT_n_SPEED", "Target Estimated Ground Speed", NITF_INT(4), true)
314               .field("TGT_n_CAT",
315                      "Target Classification Category",
316                      NITF_ENUM(1,
317                                vil_nitf2_enum_values()
318                                  .value("H", "Helicopter")
319                                  .value("T", "Tracked")
320                                  .value("U", "Unknown")
321                                  .value("W", "Wheeled")),
322                      true))
323     .field("TEST_NEG_COND",
324            "Test False Condition",
325            NITF_STR_BCSA(14),
326            false,
327            nullptr,
328            new vil_nitf2_field_value_greater_than<int>("MTI_DP", 5))
329     .field("TEST_POS_COND",
330            "Test True Condition",
331            NITF_STR_BCSA(14),
332            false,
333            nullptr,
334            new vil_nitf2_field_value_greater_than<int>("MTI_DP", 1))
335     .field("CLASS",
336            "Security Classification",
337            NITF_ENUM(1,
338                      vil_nitf2_enum_values()
339                        .value("T", "Top Secret")
340                        .value("S", "Secret")
341                        .value("C", "Confindential")
342                        .value("R", "Restricted")
343                        .value("U", "Unclassified")),
344            true,
345            nullptr,
346            nullptr)
347     .field("CODEW",
348            "Code Words",
349            NITF_STR_BCSA(15),
350            false,
351            nullptr,
352            new vil_nitf2_field_value_one_of<std::string>("CLASS", "T"))
353     .field("CWTEST",
354            "Another Code Word Test",
355            NITF_STR_BCSA(15),
356            false,
357            nullptr,
358            new vil_nitf2_field_value_one_of<std::string>("CLASS", "U"))
359     .field("NBANDS", "Number of bands", NITF_INT(1), false, nullptr, nullptr)
360     .field("XBANDS",
361            "Large number of bands",
362            NITF_INT(2),
363            false,
364            nullptr,
365            new vil_nitf2_field_value_one_of<int>("NBANDS", 0))
366     .repeat(new vil_nitf2_choose_field_value<int>(
367               "NBANDS", "XBANDS", new vil_nitf2_field_value_greater_than<int>("NBANDS", 0)),
368             vil_nitf2_field_definitions().field("BAND_LTR", "Band Description", NITF_CHAR(), true, nullptr))
369     .field("EXP_TEST", "Exponential format test", NITF_EXP(6, 1))
370     // test nested repeats and functor references to tags within and
371     // outside repeat loops
372     .field("N", "Test repeat N", NITF_INT(1))
373     .repeat(new vil_nitf2_field_value<int>("N"),
374             vil_nitf2_field_definitions()
375               .field("A", "Test repeat A", NITF_INT(1))
376               .repeat(new vil_nitf2_field_value<int>("N"),
377                       vil_nitf2_field_definitions().field("S", "Test repeat S", NITF_STR(3)))
378               .repeat(new vil_nitf2_field_value<int>("A"),
379                       vil_nitf2_field_definitions()
380                         .field("B", "Test repeat B", NITF_STR_BCSA(3))
381                         .repeat(new vil_nitf2_field_value<int>("A"),
382                                 vil_nitf2_field_definitions().field("C", "Test repeat C", NITF_STR_BCSA(4)))))
383     // test fixed repeat count
384     .repeat(4, vil_nitf2_field_definitions().field("D", "Test fixed repeat", NITF_INT(1)))
385     .end();
386   // Create a test input std::string
387   std::string testFieldsStr = "02"                    // MTI_DP
388                               "003"                   // MTI_PACKET_ID
389                                                       //"19990908070605"         // DATIME
390                               "              "        // DATIME
391                               "+89.111111-159.222222" // ACFT_LOC
392                               "890102.33N0091122.00W" // ACFT_LOC2
393                               "-60.00"                // SQUINT_ANGLE
394                               "003"                   // NO_VALID_TARGETS
395                               " "                     // TGT_CAT
396                               "2222"
397                               "H" // TGT_1_SPEED2, TGT_1_CAT2
398                               "    "
399                               " " // TGT_2_SPEED2, TGT_2_CAT2
400                               "4444"
401                               "T"               // TGT_3_SPEED2, TGT_3_CAT2
402                               ""                // TEST_NEG_COND not present
403                               "True Condition"  // TEST_POS_COND
404                               "T"               // CLASS
405                               "RandomCodeWords" // CODEW (only present if CLASS=T)
406                               ""                // CWTEST (not present because CLASS!=U
407                               "0"               // NBANDS (0, so use XBANDS instead)
408                               "12"              // XBANDS (present because NBANDS=0)
409                               "abcdefghijkl"    // 12 BAND_LTRs (XBAND=12)
410                               "+1.234567E-8"    // Exponential format test
411                               // test nested repeats
412                               "2" // N
413                               // for i=0...N-1: i=0
414                               "1"   // A[0]
415                               "S00" // S[0,0]
416                               "S01" // S[0,1]
417                               //   for j=0...A[i]-1: j=0
418                               "B00" // B[0,0]
419                               //     for k=0..A[i]-1: k=0
420                               "C000" // C[0,0,0]
421                               // i=1:
422                               "2"   // A[1]
423                               "S10" // S[1,0]
424                               "S11" // S[1,1]
425                               //   for j=0..A[i]: j=0
426                               "B10" // B[1,0]
427                               //     for k=0..A[i]
428                               "C100" // C[1,0,0]
429                               "C101" // C[1,0,1]
430                               "B11"  // B[1,1]
431                               "C110" // C[1,1,0]
432                               "C111" // C[1,1,1]
433                               // test fixed repeat
434                               "7890";
435   std::stringstream test_stream;
436   test_stream << test_tre_tag;                                                // CETAG
437   test_stream << std::setw(5) << std::setfill('0') << testFieldsStr.length(); // CELENGTH
438   test_stream << testFieldsStr;                                               // rest of fields
439   std::string read_string = test_stream.str();
440   // Write the test input std::string to a vil_stream
441   auto * vs = new vil_stream_core();
442   vs->write(read_string.c_str(), read_string.length());
443   vs->seek(0);
444   auto * vss = new vil_stream_section(vs, 0, int(read_string.length()));
445   // Record from the vil_stream
446   vil_nitf2_tagged_record * record = vil_nitf2_tagged_record::create(*vss);
447   if (record)
448   {
449     std::cerr << *record << '\n';
450     // Now write the record, and compare the output to the test input
451     std::cerr << "\nOriginal string:\n" << read_string << "\nWrite() output:\n";
452     auto * vs2 = new vil_stream_core();
453     record->write(*(vil_stream *)vs2);
454     vil_streampos bufsize = vs2->file_size();
455     char * buf = new char[(unsigned int)bufsize + 1];
456     vs2->seek(0);
457     vs2->read(buf, bufsize);
458     buf[bufsize] = '\0';
459     std::string write_string = std::string(buf);
460     std::cerr << write_string << '\n';
461     if (read_string != write_string)
462     {
463       std::cerr << "\nWrite failed!\n";
464       error = true;
465     }
466     delete[] buf;
467     std::cerr << "Testing get_value:\n";
468     int mti_dp;
469     if (!record->get_value("MTI_DP", mti_dp) || mti_dp != 2)
470     {
471       std::cerr << "Get value failed!\n";
472       error = true;
473     }
474     else
475     {
476       std::cerr << "MTI_DP = " << mti_dp << '\n';
477     }
478     int tgt_speed[4];
479     if (!record->get_value("TGT_n_SPEED", vil_nitf2_index_vector(0), tgt_speed[0]) || tgt_speed[0] != 2222 ||
480         record->get_value("TGT_n_SPEED", vil_nitf2_index_vector(1), tgt_speed[1]) /*should be null*/ ||
481         !record->get_value("TGT_n_SPEED", vil_nitf2_index_vector(2), tgt_speed[2]) || tgt_speed[2] != 4444)
482     {
483       std::cerr << "Get vector value test failed!\n";
484       error = true;
485     }
486     else
487     {
488       std::cerr << "TGT_2_SPEED = " << tgt_speed[2] << '\n';
489     }
490     int d2;
491     if (!record->get_value("D", vil_nitf2_index_vector(2), d2) || d2 != 9)
492     {
493       std::cerr << "Get fixed repeat count test failed!\n";
494       error = true;
495     }
496     // fetch C[*]
497     std::cerr << "Testing get_values (all values)...\n";
498     std::vector<std::string> c_values;
499     if (!record->get_values("C", c_values) || c_values.size() != 5 || c_values[0] != "C000" || c_values[1] != "C100" ||
500         c_values[2] != "C101" || c_values[3] != "C110" || c_values[4] != "C111")
501     {
502       std::cerr << "failed!\n\n";
503       error = true;
504     }
505     // Fetch A[1,*]
506     std::cerr << "Get values (partial index)...\n";
507     vil_nitf2_index_vector indexes;
508     std::vector<int> a_values;
509     indexes.push_back(1);
510     if (!record->get_values("A", indexes, a_values) || a_values.size() != 1 || a_values[0] != 2)
511     {
512       std::cerr << "failed!\n\n";
513       error = true;
514     }
515     // Fetch C[1,*]
516     if (!record->get_values("C", indexes, c_values) || c_values.size() != 4 || c_values[0] != "C100" ||
517         c_values[1] != "C101" || c_values[2] != "C110" || c_values[3] != "C111")
518     {
519       std::cerr << "failed!\n\n";
520     }
521   }
522   else
523   {
524     std::cerr << "Didn't create record!\n";
525     error = true;
526   }
527   // Try output of vector field
528   std::cerr << "Output of vector field C:\n" << *(record->get_field("C"));
529   // Clean up test definition and test cleanup
530   if (!vil_nitf2_tagged_record_definition::undefine(test_tre_tag))
531   {
532     std::cerr << "Error undefining TRE.\n";
533     error = true;
534   }
535   return !error;
536 }
537 
538 std::ostream &
output(std::ostream & os) const539 vil_nitf2_tagged_record::output(std::ostream & os) const
540 {
541   os << "CETAG: " << name() << '\n' << "CELEN: " << length() << std::endl;
542   for (auto & m_field_definition : *m_definition->m_field_definitions)
543   {
544     vil_nitf2_field_definition * field_def = m_field_definition->field_definition();
545     // to do: handle other nodes
546     if (!field_def)
547       break;
548     vil_nitf2_field * field = get_field(field_def->tag);
549     os << field_def->tag << ": ";
550     if (field)
551     {
552       os << *field << std::endl;
553     }
554     else
555     {
556       os << "(undefined)" << std::endl;
557     }
558   }
559   return os;
560 }
561 
562 bool
write(vil_nitf2_ostream & output)563 vil_nitf2_tagged_record::write(vil_nitf2_ostream & output)
564 {
565   // To track of how much is written
566   vil_streampos start = output.tell();
567   // Write tag and length fields
568   if (m_tag_field && m_length_field)
569   {
570     m_tag_field->write(output);
571     m_length_field->write(output);
572   }
573   else
574     return false;
575   // Write data fields
576   m_field_sequence->write(output);
577   // Check whether the std::right amount was written
578   vil_streampos end = output.tell();
579   vil_streampos length_written = end - start;
580   int expected_length = s_tag_formatter().field_width + s_length_formatter().field_width + length();
581   return length_written == expected_length;
582 }
583 
~vil_nitf2_tagged_record()584 vil_nitf2_tagged_record::~vil_nitf2_tagged_record()
585 {
586   delete m_field_sequence;
587 }
588 
589 vil_nitf2_field_definition *
find_field_definition(const std::string & tag)590 vil_nitf2_field_sequence::find_field_definition(const std::string & tag)
591 {
592   for (auto m_field_definition : *m_field_definitions)
593   {
594     vil_nitf2_field_definition * field_def = m_field_definition->field_definition();
595     // to do: search other nodes
596     if (!field_def)
597       break;
598 
599     if (field_def->tag == tag)
600     {
601       return field_def;
602     }
603   }
604   // tag definition not found
605   return nullptr;
606 }
607 
608 vil_nitf2_field::field_tree *
get_tree() const609 vil_nitf2_tagged_record::get_tree() const
610 {
611   // create our tree
612   // we add the field definitions if the TRE was recognized, or we note that we
613   // skipped it otherwise
614   vil_nitf2_field::field_tree * tr;
615   if (m_field_sequence)
616   {
617     tr = m_field_sequence->get_tree();
618   }
619   else
620   {
621     tr = new vil_nitf2_field::field_tree;
622     auto * skipped_node = new vil_nitf2_field::field_tree;
623     skipped_node->columns.emplace_back("CEDATA");
624     skipped_node->columns.emplace_back("<skipped unknown TRE>");
625     tr->children.push_back(skipped_node);
626   }
627 
628   // add the columns describing the name of the TRE
629   tr->columns.push_back(name());
630   tr->columns.push_back(pretty_name());
631   // add the CEL (length) field to the front
632   auto * first_child = new vil_nitf2_field::field_tree;
633   first_child->columns.emplace_back("CEL");
634   first_child->columns.emplace_back("Extension Length");
635   std::stringstream len_stream;
636   len_stream << length();
637   first_child->columns.push_back(len_stream.str());
638   tr->children.insert(tr->children.begin(), first_child);
639   return tr;
640 }
641 
642 std::ostream &
operator <<(std::ostream & os,const vil_nitf2_tagged_record & record)643 operator<<(std::ostream & os, const vil_nitf2_tagged_record & record)
644 {
645   return record.output(os);
646 }
647 
648 // vil_nitf2_tagged_record_sequence
649 
650 std::ostream &
operator <<(std::ostream & os,const vil_nitf2_tagged_record_sequence & seq)651 operator<<(std::ostream & os, const vil_nitf2_tagged_record_sequence & seq)
652 {
653   os << seq.size() << " TRE's:" << std::endl;
654   vil_nitf2_tagged_record_sequence::const_iterator it;
655   for (it = seq.begin(); it != seq.end(); ++it)
656   {
657     os << *it << std::endl;
658   }
659   return os;
660 }
661