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