OdbDesignLib
OdbDesign ODB++ Parsing Library
ComponentsFile.cpp
1 #include "ComponentsFile.h"
2 #include "ComponentsFile.h"
3 #include "ComponentsFile.h"
4 #include "ComponentsFile.h"
5 #include "ComponentsFile.h"
6 #include "ComponentsFile.h"
7 #include "ComponentsFile.h"
8 #include <fstream>
9 #include <sstream>
10 #include <filesystem>
11 #include "ArchiveExtractor.h"
12 #include "../parse_error.h"
13 #include <Logger.h>
14 #include <exception>
15 #include "str_utils.h"
16 #include "../invalid_odb_error.h"
17 #include <climits>
18 
19 using namespace std::filesystem;
20 
21 
22 namespace Odb::Lib::FileModel::Design
23 {
24  ComponentsFile::ComponentsFile()
25  : m_id((unsigned int)-1)
26  , m_side(BoardSide::BsNone)
27  {
28  }
29 
30  ComponentsFile::~ComponentsFile()
31  {
32  m_attributeNames.clear();
33  m_attributeTextValues.clear();
34  m_componentRecords.clear();
35  m_componentRecordsByName.clear();
36  m_bomDescriptionRecordsByCpn.clear();
37  }
38 
39  std::string ComponentsFile::GetUnits() const
40  {
41  return m_units;
42  }
43 
44  BoardSide ComponentsFile::GetSide() const
45  {
46  return m_side;
47  }
48 
49  std::filesystem::path ComponentsFile::GetPath()
50  {
51  return m_path;
52  }
53 
54  std::filesystem::path ComponentsFile::GetDirectory()
55  {
56  return m_directory;
57  }
58 
59  std::string ComponentsFile::GetLayerName() const
60  {
61  return m_layerName;
62  }
63 
64  const ComponentsFile::ComponentRecord::Vector& ComponentsFile::GetComponentRecords() const
65  {
66  return m_componentRecords;
67  }
68 
69  const ComponentsFile::ComponentRecord::StringMap& ComponentsFile::GetComponentRecordsByName() const
70  {
71  return m_componentRecordsByName;
72  }
73 
74  const std::vector<std::string>& ComponentsFile::GetAttributeNames() const
75  {
76  return m_attributeNames;
77  }
78 
79  const std::vector<std::string>& ComponentsFile::GetAttributeTextValues() const
80  {
81  return m_attributeTextValues;
82  }
83 
84  const ComponentsFile::BomDescriptionRecord::StringMap& ComponentsFile::GetBomDescriptionRecordsByCpn() const
85  {
86  return m_bomDescriptionRecordsByCpn;
87  }
88 
89  std::unique_ptr<Odb::Lib::Protobuf::ComponentsFile> ComponentsFile::to_protobuf() const
90  {
91  std::unique_ptr<Odb::Lib::Protobuf::ComponentsFile> pComponentsFileMessage(new Odb::Lib::Protobuf::ComponentsFile);
92  pComponentsFileMessage->set_units(m_units);
93  pComponentsFileMessage->set_id(m_id);
94  pComponentsFileMessage->set_side(static_cast<Odb::Lib::Protobuf::BoardSide>(m_side));
95  pComponentsFileMessage->set_layername(m_layerName);
96  pComponentsFileMessage->set_path(m_path.string());
97  pComponentsFileMessage->set_directory(m_directory.string());
98 
99  for (const auto& attributeName : m_attributeNames)
100  {
101  pComponentsFileMessage->add_attributenames(attributeName);
102  }
103 
104  for (const auto& attributeTextValue : m_attributeTextValues)
105  {
106  pComponentsFileMessage->add_attributetextvalues(attributeTextValue);
107  }
108 
109  for (const auto& pComponentRecord : m_componentRecords)
110  {
111  auto pComponentRecordMessage = pComponentRecord->to_protobuf();
112  pComponentsFileMessage->add_componentrecords()->CopyFrom(*pComponentRecordMessage);
113  }
114 
115  for (const auto& kvBomDescriptionRecord : m_bomDescriptionRecordsByCpn)
116  {
117  auto pBomDescriptionRecordMessage = kvBomDescriptionRecord.second->to_protobuf();
118  (*pComponentsFileMessage->mutable_bomdescriptionrecordsbycpn())[kvBomDescriptionRecord.first] = *pBomDescriptionRecordMessage;
119  }
120 
121  return pComponentsFileMessage;
122  }
123 
124  void ComponentsFile::from_protobuf(const Odb::Lib::Protobuf::ComponentsFile& message)
125  {
126  m_units = message.units();
127  m_id = message.id();
128  m_side = static_cast<BoardSide>(message.side());
129  m_layerName = message.layername();
130  m_path = message.path();
131  m_directory = message.directory();
132 
133  for (const auto& attributeName : message.attributenames())
134  {
135  m_attributeNames.push_back(attributeName);
136  }
137 
138  for (const auto& attributeTextValue : message.attributetextvalues())
139  {
140  m_attributeTextValues.push_back(attributeTextValue);
141  }
142 
143  for (const auto& componentRecordMessage : message.componentrecords())
144  {
145  auto pComponentRecord = std::make_shared<ComponentRecord>();
146  pComponentRecord->from_protobuf(componentRecordMessage);
147  m_componentRecords.push_back(pComponentRecord);
148  }
149 
150  for (const auto& kvBomDescriptionRecord : message.bomdescriptionrecordsbycpn())
151  {
152  auto pBomDescriptionRecord = std::make_shared<BomDescriptionRecord>();
153  pBomDescriptionRecord->from_protobuf(kvBomDescriptionRecord.second);
154  m_bomDescriptionRecordsByCpn[kvBomDescriptionRecord.first] = pBomDescriptionRecord;
155  }
156  }
157 
158  std::unique_ptr<Odb::Lib::Protobuf::ComponentsFile::BomDescriptionRecord> ComponentsFile::BomDescriptionRecord::to_protobuf() const
159  {
160  std::unique_ptr<Odb::Lib::Protobuf::ComponentsFile::BomDescriptionRecord> pBomDescriptionRecordMessage(new Odb::Lib::Protobuf::ComponentsFile::BomDescriptionRecord);
161  pBomDescriptionRecordMessage->set_cpn(cpn);
162  pBomDescriptionRecordMessage->set_pkg(pkg);
163  pBomDescriptionRecordMessage->set_ipn(ipn);
164  for (const auto& description : descriptions)
165  {
166  pBomDescriptionRecordMessage->add_descriptions()->assign(description);
167  }
168  pBomDescriptionRecordMessage->set_vpl_vnd(vpl_vnd);
169  pBomDescriptionRecordMessage->set_vpl_mpn(vpl_mpn);
170  pBomDescriptionRecordMessage->set_vnd(vnd);
171  pBomDescriptionRecordMessage->set_mpn(mpn);
172  return pBomDescriptionRecordMessage;
173  }
174 
175  void ComponentsFile::BomDescriptionRecord::from_protobuf(const Odb::Lib::Protobuf::ComponentsFile::BomDescriptionRecord& message)
176  {
177  cpn = message.cpn();
178  pkg = message.pkg();
179  ipn = message.ipn();
180  for (const auto& description : message.descriptions())
181  {
182  descriptions.push_back(description);
183  }
184  vpl_vnd = message.vpl_vnd();
185  vpl_mpn = message.vpl_mpn();
186  vnd = message.vnd();
187  mpn = message.mpn();
188  }
189 
190  ComponentsFile::ComponentRecord::~ComponentRecord()
191  {
192  m_toeprintRecords.clear();
193  m_propertyRecords.clear();
194  }
195 
196  std::unique_ptr<Odb::Lib::Protobuf::ComponentsFile::ComponentRecord> ComponentsFile::ComponentRecord::to_protobuf() const
197  {
198  std::unique_ptr<Odb::Lib::Protobuf::ComponentsFile::ComponentRecord> pComponentRecordMessage(new Odb::Lib::Protobuf::ComponentsFile::ComponentRecord);
199  pComponentRecordMessage->set_pkgref(pkgRef);
200  pComponentRecordMessage->set_locationx(locationX);
201  pComponentRecordMessage->set_locationy(locationY);
202  pComponentRecordMessage->set_rotation(rotation);
203  pComponentRecordMessage->set_mirror(mirror);
204  pComponentRecordMessage->set_compname(compName);
205  pComponentRecordMessage->set_partname(partName);
206  //pComponentRecordMessage->set_attributes(attributes);
207  pComponentRecordMessage->set_index(index);
208 
209  for (const auto& pPropertyRecord : m_propertyRecords)
210  {
211  auto pPropertyRecordMessage = pPropertyRecord->to_protobuf();
212  pComponentRecordMessage->add_propertyrecords()->CopyFrom(*pPropertyRecordMessage);
213  }
214 
215  for (const auto& pToeprintRecord : m_toeprintRecords)
216  {
217  auto pToeprintRecordMessage = pToeprintRecord->to_protobuf();
218  pComponentRecordMessage->add_toeprintrecords()->CopyFrom(*pToeprintRecordMessage);
219  }
220 
221  for (const auto& kvAttributeAssignment : m_attributeLookupTable)
222  {
223  (*pComponentRecordMessage->mutable_attributelookuptable())[kvAttributeAssignment.first] = kvAttributeAssignment.second;
224  }
225 
226  return pComponentRecordMessage;
227  }
228 
229  void ComponentsFile::ComponentRecord::from_protobuf(const Odb::Lib::Protobuf::ComponentsFile::ComponentRecord& message)
230  {
231  pkgRef = message.pkgref();
232  locationX = message.locationx();
233  locationY = message.locationy();
234  rotation = message.rotation();
235  mirror = message.mirror();
236  compName = message.compname();
237  partName = message.partname();
238  //attributes = message.attributes();
239  index = message.index();
240 
241  for (const auto& propertyRecordMessage : message.propertyrecords())
242  {
243  auto pPropertyRecord = std::make_shared<PropertyRecord>();
244  pPropertyRecord->from_protobuf(propertyRecordMessage);
245  m_propertyRecords.push_back(pPropertyRecord);
246  }
247 
248  for (const auto& toeprintRecordMessage : message.toeprintrecords())
249  {
250  auto pToeprintRecord = std::make_shared<ToeprintRecord>();
251  pToeprintRecord->from_protobuf(toeprintRecordMessage);
252  m_toeprintRecords.push_back(pToeprintRecord);
253  }
254 
255  for (const auto& kvAttributeAssignment : message.attributelookuptable())
256  {
257  m_attributeLookupTable[kvAttributeAssignment.first] = kvAttributeAssignment.second;
258  }
259  }
260 
261  std::unique_ptr<Odb::Lib::Protobuf::ComponentsFile::ComponentRecord::ToeprintRecord> ComponentsFile::ComponentRecord::ToeprintRecord::to_protobuf() const
262  {
263  std::unique_ptr<Odb::Lib::Protobuf::ComponentsFile::ComponentRecord::ToeprintRecord> pToeprintRecordMessage(new Odb::Lib::Protobuf::ComponentsFile::ComponentRecord::ToeprintRecord);
264  pToeprintRecordMessage->set_pinnumber(pinNumber);
265  pToeprintRecordMessage->set_locationx(locationX);
266  pToeprintRecordMessage->set_locationy(locationY);
267  pToeprintRecordMessage->set_rotation(rotation);
268  pToeprintRecordMessage->set_mirror(mirror);
269  pToeprintRecordMessage->set_netnumber(netNumber);
270  pToeprintRecordMessage->set_subnetnumber(subnetNumber);
271  pToeprintRecordMessage->set_name(name);
272  return pToeprintRecordMessage;
273  }
274 
275  void ComponentsFile::ComponentRecord::ToeprintRecord::from_protobuf(const Odb::Lib::Protobuf::ComponentsFile::ComponentRecord::ToeprintRecord& message)
276  {
277  pinNumber = message.pinnumber();
278  locationX = message.locationx();
279  locationY = message.locationy();
280  rotation = message.rotation();
281  mirror = message.mirror();
282  netNumber = message.netnumber();
283  subnetNumber = message.subnetnumber();
284  name = message.name();
285  }
286 
287  bool ComponentsFile::Parse(std::filesystem::path directory)
288  {
289  std::ifstream componentsFile;
290  int lineNumber = 0;
291  std::string line;
292 
293  try
294  {
295  m_directory = directory;
296 
297  m_layerName = m_directory.filename().string();
298  if (m_layerName == TOP_COMPONENTS_LAYER_NAME ||
299  m_layerName == BOTTOM_COMPONENTS_LAYER_NAME)
300  {
301  m_side = m_layerName == TOP_COMPONENTS_LAYER_NAME ?
302  BoardSide::Top :
303  BoardSide::Bottom;
304  }
305  else
306  {
307  // not a components layer
308  return true;
309  }
310 
311  loginfo("checking for extraction...");
312 
313  std::filesystem::path componentsFilePath;
314  for (const std::string componentsFilename : COMPONENTS_FILENAMES)
315  {
316  loginfo("trying components file: [" + componentsFilename + "]...");
317 
318  componentsFilePath = Utils::ArchiveExtractor::getUncompressedFilePath(m_directory, componentsFilename);
319  if (exists(componentsFilePath) && is_regular_file(componentsFilePath))
320  {
321  loginfo("found components file: [" + componentsFilePath.string() + "]");
322  break;
323  }
324  }
325 
326  m_path = componentsFilePath;
327 
328  loginfo("any extraction complete, parsing data...");
329 
330  if (!std::filesystem::exists(m_path))
331  {
332  auto message = "components file does not exist: [" + m_path.string() + "]";
333  throw invalid_odb_error(message.c_str());
334  }
335  else if (!std::filesystem::is_regular_file(m_path))
336  {
337  auto message = "components is not a file: [" + m_path.string() + "]";
338  throw invalid_odb_error(message.c_str());
339  }
340 
341  componentsFile.open(m_path.string(), std::ios::in);
342  if (!componentsFile.is_open())
343  {
344  auto message = "unable to open components file: [" + m_path.string() + "]";
345  throw invalid_odb_error(message.c_str());
346  }
347 
348  std::shared_ptr<ComponentRecord> pCurrentComponentRecord;
349  std::shared_ptr<BomDescriptionRecord> pCurrentBomDescriptionRecord;
350 
351  while (std::getline(componentsFile, line))
352  {
353  lineNumber++;
354 
355  // trim whitespace from beginning and end of line
356  Utils::str_trim(line);
357  if (!line.empty())
358  {
359  std::stringstream lineStream(line);
360  //char firstChar = line[0];
361 
362  if (line.find(COMMENT_TOKEN) == 0)
363  {
364  // comment line
365  }
366  else if (line.find(UNITS_TOKEN) == 0)
367  {
368  // units line
369  std::string token;
370  if (!std::getline(lineStream, token, '='))
371  {
372  throw_parse_error(m_path, line, token, lineNumber);
373  }
374  else if (!std::getline(lineStream, token, '='))
375  {
376  throw_parse_error(m_path, line, token, lineNumber);
377  }
378 
379  m_units = token;
380  }
381  else if (line.find(ID_TOKEN) == 0)
382  {
383  std::string token;
384  if (!std::getline(lineStream, token, '='))
385  {
386  throw_parse_error(m_path, line, token, lineNumber);
387  }
388  else if (!std::getline(lineStream, token, '='))
389  {
390  throw_parse_error(m_path, line, token, lineNumber);
391  }
392  m_id = std::stoul(token);
393  }
394  else if (line.find(ATTRIBUTE_NAME_TOKEN) == 0)
395  {
396  // component attribute name line
397  std::string token;
398  // TODO: continue on failing line parse, to make a less strict/more robust parser (make a flag: enum ParseStrictness { strict, lax })
399  if (!std::getline(lineStream, token, ' '))
400  {
401  throw_parse_error(m_path, line, token, lineNumber);
402  }
403  else if (!std::getline(lineStream, token, ' '))
404  {
405  throw_parse_error(m_path, line, token, lineNumber);
406  }
407  m_attributeNames.push_back(token);
408  }
409  else if (line.find(ATTRIBUTE_VALUE_TOKEN) == 0)
410  {
411  // component attribute text string values
412  std::string token;
413  if (!std::getline(lineStream, token, ' '))
414  {
415  throw_parse_error(m_path, line, token, lineNumber);
416  }
417  else if (!std::getline(lineStream, token, ' '))
418  {
419  throw_parse_error(m_path, line, token, lineNumber);
420  }
421  m_attributeTextValues.push_back(token);
422  }
423  else if (line.find(ComponentRecord::RECORD_TOKEN) == 0)
424  {
425  // component record line
426  std::string token;
427 
428  lineStream >> token;
429  if (token != ComponentRecord::RECORD_TOKEN)
430  {
431  throw_parse_error(m_path, line, token, lineNumber);
432  }
433 
434  if (pCurrentBomDescriptionRecord != nullptr)
435  {
436  // BomDescriptionRecord end (i.e. MPN) line
437  m_bomDescriptionRecordsByCpn[pCurrentBomDescriptionRecord->cpn] = pCurrentBomDescriptionRecord;
438  pCurrentBomDescriptionRecord.reset();
439  }
440 
441  // do we have a current component record?
442  if (pCurrentComponentRecord != nullptr)
443  {
444  // finish it up and add it to the list
445  m_componentRecords.push_back(pCurrentComponentRecord);
446  pCurrentComponentRecord.reset();
447  }
448 
449  // create a new current component record
450  pCurrentComponentRecord = std::make_shared<ComponentRecord>();
451 
452  lineStream >> pCurrentComponentRecord->pkgRef;
453  lineStream >> pCurrentComponentRecord->locationX;
454  lineStream >> pCurrentComponentRecord->locationY;
455  lineStream >> pCurrentComponentRecord->rotation;
456 
457  char mirror;
458  lineStream >> mirror;
459  pCurrentComponentRecord->mirror = (mirror == 'M');
460 
461  lineStream >> pCurrentComponentRecord->compName;
462  lineStream >> pCurrentComponentRecord->partName;
463  //lineStream >> pCurrentComponentRecord->attributes;
464 
465  if (m_componentRecords.size() > UINT_MAX)
466  {
467  throw_parse_error(m_path, line, token, lineNumber);
468  }
469 
470  pCurrentComponentRecord->index = static_cast<unsigned int>(m_componentRecords.size());
471 
472  std::string attrIdString;
473  lineStream >> attrIdString;
474 
475  if (!pCurrentComponentRecord->ParseAttributeLookupTable(attrIdString))
476  {
477  throw_parse_error(m_path, line, token, lineNumber);
478  }
479  }
480  else if (line.find(PropertyRecord::RECORD_TOKEN) == 0)
481  {
482  // component property record line
483  std::string token;
484  if (!(lineStream >> token))
485  {
486  throw_parse_error(m_path, line, token, lineNumber);
487  }
488 
489  if (token != PropertyRecord::RECORD_TOKEN)
490  {
491  throw_parse_error(m_path, line, token, lineNumber);
492  }
493 
494  auto pPropertyRecord = std::make_shared<PropertyRecord>();
495 
496  if (!(lineStream >> pPropertyRecord->name))
497  {
498  throw_parse_error(m_path, line, token, lineNumber);
499  }
500 
501  if (!(lineStream >> token))
502  {
503  throw_parse_error(m_path, line, token, lineNumber);
504  }
505 
506  if (!token.empty())
507  {
508  // handle case where the beginning of the value, after the opening single-quote, is whitespace...
509  if (token == "'")
510  {
511  // eat the single-quote and get the next token, i.e. the actual value (w/ space in front)
512  if (!(lineStream >> token))
513  {
514  throw_parse_error(m_path, line, token, lineNumber);
515  }
516  }
517 
518  if (token.size() > 0 && token[0] == '\'')
519  {
520  // remove leading quote
521  token.erase(0, 1);
522  }
523 
524  if (token.size() > 0 && token[token.size() - 1] == '\'')
525  {
526  // remove trailing quote
527  token.erase(token.size() - 1);
528  }
529 
530  Utils::str_trim(token);
531  }
532 
533  pPropertyRecord->value = token;
534 
535  float f;
536  while (lineStream >> f)
537  {
538  pPropertyRecord->floatValues.push_back(f);
539  }
540 
541  pCurrentComponentRecord->m_propertyRecords.push_back(pPropertyRecord);
542  }
543  else if (line.find(ComponentRecord::ToeprintRecord::RECORD_TOKEN) == 0)
544  {
545  // component toeprint record line
546  std::string token;
547  lineStream >> token;
548  if (token != ComponentRecord::ToeprintRecord::RECORD_TOKEN)
549  {
550  throw_parse_error(m_path, line, token, lineNumber);
551  }
552 
553  auto pToeprintRecord = std::make_shared<ComponentRecord::ToeprintRecord>();
554  lineStream >> pToeprintRecord->pinNumber;
555  lineStream >> pToeprintRecord->locationX;
556  lineStream >> pToeprintRecord->locationY;
557  lineStream >> pToeprintRecord->rotation;
558 
559  char mirror;
560  lineStream >> mirror;
561  pToeprintRecord->mirror = (mirror == 'M' || mirror == 'm');
562 
563  lineStream >> pToeprintRecord->netNumber;
564  if (pToeprintRecord->netNumber == (unsigned int)-1)
565  {
566  // netNumber == -1
567  parse_info pi(m_path, line, lineNumber);
568  logdebug(pi.toString("Component Toeprint record with netNumber = -1"));
569 
570  if (!m_allowToepintNetNumbersOfNegative1)
571  {
572  throw_parse_error(m_path, line, token, lineNumber);
573  }
574  }
575 
576  lineStream >> pToeprintRecord->subnetNumber;
577  if (pToeprintRecord->subnetNumber == (unsigned int)-1)
578  {
579  // subnetNumber == -1
580  parse_info pi(m_path, line, lineNumber);
581  logdebug(pi.toString("Component Toeprint record with subnetNumber = -1"));
582 
583  if (!m_allowToepintNetNumbersOfNegative1)
584  {
585  throw_parse_error(m_path, line, token, lineNumber);
586  }
587  }
588 
589  lineStream >> pToeprintRecord->name;
590 
591  pCurrentComponentRecord->m_toeprintRecords.push_back(pToeprintRecord);
592  }
593  else if (line.find(ComponentsFile::BomDescriptionRecord::CPN_RECORD_TOKEN) == 0)
594  {
595  // BomDescriptionRecord start (i.e. CPN) line
596 
597  std::string token;
598  lineStream >> token;
599  if (token != ComponentsFile::BomDescriptionRecord::CPN_RECORD_TOKEN)
600  {
601  throw_parse_error(m_path, line, token, lineNumber);
602  }
603 
604  if (pCurrentBomDescriptionRecord != nullptr)
605  {
606  m_bomDescriptionRecordsByCpn[pCurrentBomDescriptionRecord->cpn] = pCurrentBomDescriptionRecord;
607  pCurrentBomDescriptionRecord.reset();
608  }
609 
610  pCurrentBomDescriptionRecord = std::make_shared<ComponentsFile::BomDescriptionRecord>();
611  }
612  else if (line.find(ComponentsFile::BomDescriptionRecord::IPN_RECORD_TOKEN) == 0)
613  {
614  std::string token;
615  lineStream >> token;
616  if (token != ComponentsFile::BomDescriptionRecord::IPN_RECORD_TOKEN)
617  {
618  throw_parse_error(m_path, line, token, lineNumber);
619  }
620 
621  if (pCurrentBomDescriptionRecord == nullptr)
622  {
623  throw_parse_error(m_path, line, token, lineNumber);
624  }
625 
626  if (!(lineStream >> pCurrentBomDescriptionRecord->ipn))
627  {
628  // blank/ no IPN value allowed
629  //throw_parse_error(m_path, line, token, lineNumber);
630  }
631  }
632  else if (line.find(ComponentsFile::BomDescriptionRecord::DSC_RECORD_TOKEN) == 0)
633  {
634  std::string token;
635  lineStream >> token;
636  if (token != ComponentsFile::BomDescriptionRecord::DSC_RECORD_TOKEN)
637  {
638  throw_parse_error(m_path, line, token, lineNumber);
639  }
640 
641  if (pCurrentBomDescriptionRecord == nullptr)
642  {
643  throw_parse_error(m_path, line, token, lineNumber);
644  }
645 
646  std::string description;
647 
648  if (!(lineStream >> description))
649  {
650  // allow blank/no description value
651  //throw_parse_error(m_path, line, token, lineNumber);
652  }
653 
654  pCurrentBomDescriptionRecord->descriptions.push_back(description);
655  }
656  else if (line.find(ComponentsFile::BomDescriptionRecord::VPL_VND_RECORD_TOKEN) == 0)
657  {
658  std::string token;
659  lineStream >> token;
660  if (token != ComponentsFile::BomDescriptionRecord::VPL_VND_RECORD_TOKEN)
661  {
662  throw_parse_error(m_path, line, token, lineNumber);
663  }
664 
665  if (pCurrentBomDescriptionRecord == nullptr)
666  {
667  throw_parse_error(m_path, line, token, lineNumber);
668  }
669 
670  if (!(lineStream >> pCurrentBomDescriptionRecord->vpl_vnd))
671  {
672  throw_parse_error(m_path, line, token, lineNumber);
673  }
674  }
675  else if (line.find(ComponentsFile::BomDescriptionRecord::VPL_MPN_RECORD_TOKEN) == 0)
676  {
677  std::string token;
678  lineStream >> token;
679  if (token != ComponentsFile::BomDescriptionRecord::VPL_MPN_RECORD_TOKEN)
680  {
681  throw_parse_error(m_path, line, token, lineNumber);
682  }
683 
684  if (pCurrentBomDescriptionRecord == nullptr)
685  {
686  throw_parse_error(m_path, line, token, lineNumber);
687  }
688 
689  if (!(lineStream >> pCurrentBomDescriptionRecord->vpl_mpn))
690  {
691  throw_parse_error(m_path, line, token, lineNumber);
692  }
693  }
694  else if (line.find(ComponentsFile::BomDescriptionRecord::VND_RECORD_TOKEN) == 0)
695  {
696  std::string token;
697  lineStream >> token;
698  if (token != ComponentsFile::BomDescriptionRecord::VND_RECORD_TOKEN)
699  {
700  throw_parse_error(m_path, line, token, lineNumber);
701  }
702 
703  if (pCurrentBomDescriptionRecord == nullptr)
704  {
705  throw_parse_error(m_path, line, token, lineNumber);
706  }
707 
708  if (!(lineStream >> pCurrentBomDescriptionRecord->vnd))
709  {
710  throw_parse_error(m_path, line, token, lineNumber);
711  }
712  }
713  else if (line.find(ComponentsFile::BomDescriptionRecord::MPN_RECORD_TOKEN) == 0)
714  {
715  std::string token;
716  lineStream >> token;
717  if (token != ComponentsFile::BomDescriptionRecord::MPN_RECORD_TOKEN)
718  {
719  throw_parse_error(m_path, line, token, lineNumber);
720  }
721 
722  if (pCurrentBomDescriptionRecord == nullptr)
723  {
724  throw_parse_error(m_path, line, token, lineNumber);
725  }
726 
727  if (!(lineStream >> pCurrentBomDescriptionRecord->mpn))
728  {
729  throw_parse_error(m_path, line, token, lineNumber);
730  }
731 
732  // BomDescriptionRecord end (i.e. MPN) line
733  //m_bomDescriptionRecordsByCpn[pCurrentBomDescriptionRecord->cpn] = pCurrentBomDescriptionRecord;
734  //pCurrentBomDescriptionRecord.reset();
735  }
736  }
737  }
738 
739  if (pCurrentBomDescriptionRecord != nullptr)
740  {
741  // BomDescriptionRecord end (i.e. MPN) line
742  m_bomDescriptionRecordsByCpn[pCurrentBomDescriptionRecord->cpn] = pCurrentBomDescriptionRecord;
743  pCurrentBomDescriptionRecord.reset();
744  }
745 
746  // do we have a current component record? (finish up the last record in the file- i.e. ran out of file)
747  if (pCurrentComponentRecord != nullptr)
748  {
749  // finish it up and add it to the list
750  m_componentRecords.push_back(pCurrentComponentRecord);
751  pCurrentComponentRecord.reset();
752  }
753 
754  componentsFile.close();
755  }
756  catch (parse_error& pe)
757  {
758  auto m = pe.toString("Parse Error:");
759  logerror(m);
760  componentsFile.close();
761  throw pe;
762  }
763  catch (std::exception& e)
764  {
765  parse_info pi(m_path, line, lineNumber);
766  const auto m = pi.toString();
767  logexception_msg(e, m);
768  componentsFile.close();
769  throw e;
770  }
771 
772  return true;
773  }
774 }