OdbDesignLib
OdbDesign ODB++ Parsing Library
EdaDataFile.cpp
1 #include "EdaDataFile.h"
2 #include <fstream>
3 #include <sstream>
4 #include "str_utils.h"
5 #include "../../enums.h"
6 #include "../../ProtoBuf/edadatafile.pb.h"
7 #include "ArchiveExtractor.h"
8 #include "Logger.h"
9 #include "../parse_info.h"
10 #include "../parse_error.h"
11 #include "../invalid_odb_error.h"
12 
13 using namespace std::filesystem;
14 using namespace Utils;
15 
16 namespace Odb::Lib::FileModel::Design
17 {
18  EdaDataFile::EdaDataFile(bool logAllLineParsing)
19  : m_logAllLineParsing(logAllLineParsing)
20  {
21  }
22 
23  EdaDataFile::~EdaDataFile()
24  {
25  m_layerNames.clear();
26  m_attributeNames.clear();
27  m_attributeTextValues.clear();
28  m_netRecords.clear();
29  m_netRecordsByName.clear();
30  m_packageRecords.clear();
31  m_packageRecordsByName.clear();
32  m_featureGroupRecords.clear();
33  m_propertyRecords.clear();
34  }
35 
36  const std::filesystem::path& EdaDataFile::GetPath() const
37  {
38  return m_path;
39  }
40 
41  const std::filesystem::path& EdaDataFile::GetDirectory() const
42  {
43  return m_directory;
44  }
45 
46  const std::string& EdaDataFile::GetUnits() const
47  {
48  return m_units;
49  }
50 
51  const std::string& EdaDataFile::GetSource() const
52  {
53  return m_source;
54  }
55 
56  EdaDataFile::NetRecord::SubnetRecord::~SubnetRecord()
57  {
58  m_featureIdRecords.clear();
59  }
60 
61  std::unique_ptr<Odb::Lib::Protobuf::EdaDataFile::NetRecord::SubnetRecord> EdaDataFile::NetRecord::SubnetRecord::to_protobuf() const
62  {
63  std::unique_ptr<Odb::Lib::Protobuf::EdaDataFile::NetRecord::SubnetRecord> pSubnetRecordMessage(new Odb::Lib::Protobuf::EdaDataFile::NetRecord::SubnetRecord);
64  pSubnetRecordMessage->set_type((Odb::Lib::Protobuf::EdaDataFile::NetRecord::SubnetRecord::Type) type);
65  if (type == Type::Toeprint)
66  {
67  pSubnetRecordMessage->set_componentnumber(componentNumber);
68  pSubnetRecordMessage->set_toeprintnumber(toeprintNumber);
69  pSubnetRecordMessage->set_side((Odb::Lib::Protobuf::BoardSide)side);
70  }
71  else if (type == Type::Plane)
72  {
73  pSubnetRecordMessage->set_filltype((Odb::Lib::Protobuf::EdaDataFile::NetRecord::SubnetRecord::FillType)fillType);
74  pSubnetRecordMessage->set_cutouttype((Odb::Lib::Protobuf::EdaDataFile::NetRecord::SubnetRecord::CutoutType) cutoutType);
75  pSubnetRecordMessage->set_fillsize(fillSize);
76  }
77 
78  for (const auto& featureIdRecord : m_featureIdRecords)
79  {
80  auto pFeatureIdRecordMessage = pSubnetRecordMessage->add_featureidrecords();
81  pFeatureIdRecordMessage->CopyFrom(*featureIdRecord->to_protobuf());
82  }
83  return pSubnetRecordMessage;
84  }
85 
86  void EdaDataFile::NetRecord::SubnetRecord::from_protobuf(const Odb::Lib::Protobuf::EdaDataFile::NetRecord::SubnetRecord& message)
87  {
88  type = static_cast<Type>(message.type());
89 
90  if (type == Type::Toeprint)
91  {
92  componentNumber = message.componentnumber();
93  toeprintNumber = message.toeprintnumber();
94  side = static_cast<BoardSide>(message.side());
95  }
96  else if (type == Type::Plane)
97  {
98  fillType = static_cast<FillType>(message.filltype());
99  cutoutType = static_cast<CutoutType>(message.cutouttype());
100  fillSize = message.fillsize();
101  index = message.index();
102  }
103 
104  for (const auto& featureIdRecordMessage : message.featureidrecords())
105  {
106  auto pFeatureIdRecord = std::make_shared<FeatureIdRecord>();
107  pFeatureIdRecord->from_protobuf(featureIdRecordMessage);
108  m_featureIdRecords.push_back(pFeatureIdRecord);
109  }
110  }
111 
112  EdaDataFile::NetRecord::~NetRecord()
113  {
114  m_subnetRecords.clear();
115  m_propertyRecords.clear();
116  }
117 
118  std::unique_ptr<Odb::Lib::Protobuf::EdaDataFile::NetRecord> EdaDataFile::NetRecord::to_protobuf() const
119  {
120  std::unique_ptr<Odb::Lib::Protobuf::EdaDataFile::NetRecord> pNetRecordMessage(new Odb::Lib::Protobuf::EdaDataFile::NetRecord);
121  pNetRecordMessage->set_name(name);
122  //pNetRecordMessage->set_attributesidstring(attributesIdString);
123  pNetRecordMessage->set_index(index);
124 
125  for (const auto& propertyRecord : m_propertyRecords)
126  {
127  auto pPropertyRecordMessage = pNetRecordMessage->add_propertyrecords();
128  pPropertyRecordMessage->CopyFrom(*propertyRecord->to_protobuf());
129  }
130 
131  for (const auto& subnetRecord : m_subnetRecords)
132  {
133  auto pSubnetRecordMessage = pNetRecordMessage->add_subnetrecords();
134  pSubnetRecordMessage->CopyFrom(*subnetRecord->to_protobuf());
135  }
136  for (const auto& kvAttributeAssignment : m_attributeLookupTable)
137  {
138  (*pNetRecordMessage->mutable_attributelookuptable())[kvAttributeAssignment.first] = kvAttributeAssignment.second;
139  }
140  return pNetRecordMessage;
141  }
142 
143  void EdaDataFile::NetRecord::from_protobuf(const Odb::Lib::Protobuf::EdaDataFile::NetRecord& message)
144  {
145  name = message.name();
146  //attributesIdString = message.attributesidstring();
147  index = message.index();
148 
149  for (const auto& propertyRecordMessage : message.propertyrecords())
150  {
151  auto pPropertyRecord = std::make_shared<PropertyRecord>();
152  pPropertyRecord->from_protobuf(propertyRecordMessage);
153  m_propertyRecords.push_back(pPropertyRecord);
154  }
155 
156  for (const auto& subnetRecordMessage : message.subnetrecords())
157  {
158  auto pSubnetRecord = std::make_shared<SubnetRecord>();
159  pSubnetRecord->from_protobuf(subnetRecordMessage);
160  m_subnetRecords.push_back(pSubnetRecord);
161  }
162 
163  for (const auto& kvAttributeAssignment : message.attributelookuptable())
164  {
165  m_attributeLookupTable[kvAttributeAssignment.first] = kvAttributeAssignment.second;
166  }
167  }
168 
169  std::unique_ptr<Odb::Lib::Protobuf::EdaDataFile::FeatureIdRecord>
170  EdaDataFile::FeatureIdRecord::to_protobuf() const
171  {
172  std::unique_ptr<Odb::Lib::Protobuf::EdaDataFile::FeatureIdRecord> pFeatureIdRecordMessage(new Odb::Lib::Protobuf::EdaDataFile::FeatureIdRecord);
173  pFeatureIdRecordMessage->set_type((Odb::Lib::Protobuf::EdaDataFile::FeatureIdRecord::Type) type);
174  pFeatureIdRecordMessage->set_layernumber(layerNumber);
175  pFeatureIdRecordMessage->set_featurenumber(featureNumber);
176  return pFeatureIdRecordMessage;
177  }
178 
179  void EdaDataFile::FeatureIdRecord::from_protobuf(const Odb::Lib::Protobuf::EdaDataFile::FeatureIdRecord& message)
180  {
181  type = static_cast<Type>(message.type());
182  layerNumber = message.layernumber();
183  featureNumber = message.featurenumber();
184  }
185 
186  std::unique_ptr<Odb::Lib::Protobuf::EdaDataFile::FeatureGroupRecord>
187  EdaDataFile::FeatureGroupRecord::to_protobuf() const
188  {
189  std::unique_ptr<Odb::Lib::Protobuf::EdaDataFile::FeatureGroupRecord> pFeatureGroupRecordMessage(new Odb::Lib::Protobuf::EdaDataFile::FeatureGroupRecord);
190  pFeatureGroupRecordMessage->set_type(type);
191  for (const auto& featureIdRecord : m_featureIdRecords)
192  {
193  auto pFeatureIdRecordMessage = pFeatureGroupRecordMessage->add_featureidrecords();
194  pFeatureIdRecordMessage->CopyFrom(*featureIdRecord->to_protobuf());
195  }
196  for (const auto& propertyRecord : m_propertyRecords)
197  {
198  auto pPropertyRecordMessage = pFeatureGroupRecordMessage->add_featureidrecords();
199  pPropertyRecordMessage->CopyFrom(*propertyRecord->to_protobuf());
200  }
201  return pFeatureGroupRecordMessage;
202  }
203 
204  void EdaDataFile::FeatureGroupRecord::from_protobuf(const Odb::Lib::Protobuf::EdaDataFile::FeatureGroupRecord& message)
205  {
206  type = message.type();
207  for (const auto& featureIdRecordMessage : message.featureidrecords())
208  {
209  auto pFeatureIdRecord = std::make_shared<FeatureIdRecord>();
210  pFeatureIdRecord->from_protobuf(featureIdRecordMessage);
211  m_featureIdRecords.push_back(pFeatureIdRecord);
212  }
213  for (const auto& propertyRecordMessage : message.propertyrecords())
214  {
215  auto pPropertyRecord = std::make_shared<PropertyRecord>();
216  pPropertyRecord->from_protobuf(propertyRecordMessage);
217  m_propertyRecords.push_back(pPropertyRecord);
218  }
219  }
220 
221  const std::vector<std::string>& EdaDataFile::GetLayerNames() const
222  {
223  return m_layerNames;
224  }
225 
226  const std::vector<std::string>& EdaDataFile::GetAttributeNames() const
227  {
228  return m_attributeNames;
229  }
230 
231  const std::vector<std::string>& EdaDataFile::GetAttributeTextValues() const
232  {
233  return m_attributeTextValues;
234  }
235 
236  const EdaDataFile::NetRecord::Vector& EdaDataFile::GetNetRecords() const
237  {
238  return m_netRecords;
239  }
240 
241  const EdaDataFile::NetRecord::StringMap& EdaDataFile::GetNetRecordsByName() const
242  {
243  return m_netRecordsByName;
244  }
245 
246  const EdaDataFile::PackageRecord::Vector& EdaDataFile::GetPackageRecords() const
247  {
248  return m_packageRecords;
249  }
250 
251  const EdaDataFile::PackageRecord::StringMap& EdaDataFile::GetPackageRecordsByName() const
252  {
253  return m_packageRecordsByName;
254  }
255 
256  const EdaDataFile::FeatureGroupRecord::Vector& EdaDataFile::GetFeatureGroupRecords() const
257  {
258  return m_featureGroupRecords;
259  }
260 
261  const PropertyRecord::Vector& EdaDataFile::GetPropertyRecords() const
262  {
263  return m_propertyRecords;
264  }
265 
266  std::unique_ptr<Odb::Lib::Protobuf::EdaDataFile> EdaDataFile::to_protobuf() const
267  {
268  std::unique_ptr<Odb::Lib::Protobuf::EdaDataFile> pEdaDataFileMessage(new Odb::Lib::Protobuf::EdaDataFile);
269  pEdaDataFileMessage->set_path(m_path.string());
270  pEdaDataFileMessage->set_units(m_units);
271  pEdaDataFileMessage->set_source(m_source);
272  for (const auto& layerName : m_layerNames)
273  {
274  pEdaDataFileMessage->add_layernames(layerName);
275  }
276  for (const auto& attributeName : m_attributeNames)
277  {
278  pEdaDataFileMessage->add_attributenames(attributeName);
279  }
280  for (const auto& attrTextValue : m_attributeTextValues)
281  {
282  pEdaDataFileMessage->add_attributetextvalues(attrTextValue);
283  }
284  for (const auto& pNetRecord : m_netRecords)
285  {
286  auto pNetRecordMessage = pEdaDataFileMessage->add_netrecords();
287  pNetRecordMessage->CopyFrom(*pNetRecord->to_protobuf());
288  }
289  for (const auto& kvNetRecord : m_netRecordsByName)
290  {
291  (*pEdaDataFileMessage->mutable_netrecordsbyname())[kvNetRecord.first] = *kvNetRecord.second->to_protobuf();
292  }
293  for (const auto& pPackageRecord : m_packageRecords)
294  {
295  auto pPackageRecordMessage = pEdaDataFileMessage->add_packagerecords();
296  pPackageRecordMessage->CopyFrom(*pPackageRecord->to_protobuf());
297  }
298  for (const auto& kvPackageRecord : m_packageRecordsByName)
299  {
300  (*pEdaDataFileMessage->mutable_packagerecordsbyname())[kvPackageRecord.first] = *kvPackageRecord.second->to_protobuf();
301  }
302  for (const auto& pPropertyRecord : m_propertyRecords)
303  {
304  auto pPropertyRecordMessage = pEdaDataFileMessage->add_propertyrecords();
305  pPropertyRecordMessage->CopyFrom(*pPropertyRecord->to_protobuf());
306  }
307  for (const auto& pFeatureGroupRecord : m_featureGroupRecords)
308  {
309  auto pFeatureGroupRecordMessage = pEdaDataFileMessage->add_featuregrouprecords();
310  pFeatureGroupRecordMessage->CopyFrom(*pFeatureGroupRecord->to_protobuf());
311  }
312 
313  return pEdaDataFileMessage;
314  }
315 
316  void EdaDataFile::from_protobuf(const Odb::Lib::Protobuf::EdaDataFile& message)
317  {
318  m_path = message.path();
319  m_units = message.units();
320  m_source = message.source();
321  for (const auto& layerName : message.layernames())
322  {
323  m_layerNames.push_back(layerName);
324  }
325  for (const auto& attributeName : message.attributenames())
326  {
327  m_attributeNames.push_back(attributeName);
328  }
329  for (const auto& attrTextValue : message.attributetextvalues())
330  {
331  m_attributeTextValues.push_back(attrTextValue);
332  }
333  for (const auto& netRecordMessage : message.netrecords())
334  {
335  auto pNetRecord = std::make_shared<NetRecord>();
336  pNetRecord->from_protobuf(netRecordMessage);
337  m_netRecords.push_back(pNetRecord);
338  }
339  for (const auto& kvNetRecordMessage : message.netrecordsbyname())
340  {
341  auto pNetRecord = std::make_shared<NetRecord>();
342  pNetRecord->from_protobuf(kvNetRecordMessage.second);
343  m_netRecordsByName[kvNetRecordMessage.first] = pNetRecord;
344  }
345  for (const auto& packageRecordMessage : message.packagerecords())
346  {
347  auto pPackageRecord = std::make_shared<PackageRecord>();
348  pPackageRecord->from_protobuf(packageRecordMessage);
349  m_packageRecords.push_back(pPackageRecord);
350  }
351  for (const auto& kvPackageRecordMessage : message.packagerecordsbyname())
352  {
353  auto pPackageRecord = std::make_shared<PackageRecord>();
354  pPackageRecord->from_protobuf(kvPackageRecordMessage.second);
355  m_packageRecordsByName[kvPackageRecordMessage.first] = pPackageRecord;
356  }
357  for (const auto& propertyRecordMessage : message.propertyrecords())
358  {
359  auto pPropertyRecord = std::make_shared<PropertyRecord>();
360  pPropertyRecord->from_protobuf(propertyRecordMessage);
361  m_propertyRecords.push_back(pPropertyRecord);
362  }
363  for (const auto& featureGroupRecordMessage : message.featuregrouprecords())
364  {
365  auto pFeatureGroupRecord = std::make_shared<FeatureGroupRecord>();
366  pFeatureGroupRecord->from_protobuf(featureGroupRecordMessage);
367  m_featureGroupRecords.push_back(pFeatureGroupRecord);
368  }
369  }
370 
371  bool EdaDataFile::Save(std::ostream& os)
372  {
373  return true;
374  }
375 
376  bool EdaDataFile::Parse(std::filesystem::path path)
377  {
378  std::ifstream edaDataFile;
379  int lineNumber = 0;
380  std::string line;
381 
382  try
383  {
384  loginfo("checking for extraction...");
385 
386  m_directory = path;
387  m_path = ArchiveExtractor::getUncompressedFilePath(m_directory.string(), EDADATA_FILENAME);
388 
389  if (!std::filesystem::exists(m_path))
390  {
391  auto message = "eda/data file does not exist: [" + m_path.string() + "]";
392  throw invalid_odb_error(message.c_str());
393  }
394  else if (!std::filesystem::is_regular_file(m_path))
395  {
396  auto message = "eda/data is not a file: [" + m_path.string() + "]";
397  throw invalid_odb_error(message.c_str());
398  }
399 
400  loginfo("any extraction complete, parsing data...");
401 
402  edaDataFile.open(m_path.string(), std::ios::in);
403  if (!edaDataFile.is_open())
404  {
405  auto message = "unable to open eda/data file: [" + m_path.string() + "]";
406  throw invalid_odb_error(message.c_str());
407  }
408 
409  std::shared_ptr<NetRecord> pCurrentNetRecord;
410  std::shared_ptr<NetRecord::SubnetRecord> pCurrentSubnetRecord;
411 
412  std::shared_ptr<PackageRecord> pCurrentPackageRecord;
413  std::shared_ptr<PackageRecord::PinRecord> pCurrentPinRecord;
414 
415  std::shared_ptr<PackageRecord::OutlineRecord> pCurrentContourOutlineRecord;
416  std::shared_ptr<ContourPolygon> pCurrentContourPolygon;
417 
418  FeatureGroupRecord::shared_ptr pCurrentFeatureGroupRecord;
419 
420  while (std::getline(edaDataFile, line))
421  {
422  // keep track of line number
423  lineNumber++;
424 
425  // trim whitespace from beginning and end of line
426  Utils::str_trim(line);
427  if (!line.empty())
428  {
429  if (m_logAllLineParsing)
430  {
431  parse_info pi(m_path, line, lineNumber);
432  logdebug(pi.toString("Parsing line..."));
433  }
434 
435  std::stringstream lineStream(line);
436 
437  if (line.find(ATTRIBUTE_NAME_TOKEN) == 0 ||
438  line.find(std::string(COMMENT_TOKEN) + ATTRIBUTE_NAME_TOKEN) == 0) // backward compatibility dictates allowing comment character in front of attribute value token
439  {
440  // component attribute name line
441  std::string token;
442  // TODO: continue on failing line parse, to make a less strict/more robust parser (make a flag: enum ParseStrictness { strict, lax })
443  if (!std::getline(lineStream, token, ' '))
444  {
445  throw_parse_error(m_path, line, token, lineNumber);
446  }
447  else if (!std::getline(lineStream, token, ' '))
448  {
449  throw_parse_error(m_path, line, token, lineNumber);
450  }
451  m_attributeNames.push_back(token);
452  }
453  else if (line.find(ATTRIBUTE_VALUE_TOKEN) == 0 ||
454  line.find(std::string(COMMENT_TOKEN) + ATTRIBUTE_VALUE_TOKEN) == 0) // backward compatibility dictates allowing comment character in front of attribute value token
455  {
456  // component attribute text string values
457  std::string token;
458  if (!std::getline(lineStream, token, ' '))
459  {
460  throw_parse_error(m_path, line, token, lineNumber);
461  }
462  else if (!std::getline(lineStream, token, ' '))
463  {
464  throw_parse_error(m_path, line, token, lineNumber);
465  }
466  m_attributeTextValues.push_back(token);
467  }
468  else if (line.find(COMMENT_TOKEN) == 0)
469  {
470  // comment line
471  // TODO: attribute lines can begin with a comment for backward compatbility
472  }
473  else if (line.find(UNITS_TOKEN) == 0)
474  {
475  // units line
476  std::string token;
477  if (!std::getline(lineStream, token, '='))
478  {
479  throw_parse_error(m_path, line, token, lineNumber);
480  }
481  else if (!std::getline(lineStream, token, '='))
482  {
483  throw_parse_error(m_path, line, token, lineNumber);
484  }
485  m_units = token;
486  }
487  else if (line.find(HEADER_RECORD_TOKEN) == 0)
488  {
489  // component record line
490  std::string token;
491 
492  lineStream >> token;
493  if (token != HEADER_RECORD_TOKEN)
494  {
495  throw_parse_error(m_path, line, token, lineNumber);
496  }
497 
498  // read the rest of the line as the source
499  if (!std::getline(lineStream, m_source))
500  {
501  throw_parse_error(m_path, line, token, lineNumber);
502  }
503 
504  Utils::str_trim(m_source);
505  }
506  else if (line.find(LAYER_NAMES_RECORD_TOKEN) == 0)
507  {
508  // component record line
509  std::string token;
510 
511  lineStream >> token;
512  if (token != LAYER_NAMES_RECORD_TOKEN)
513  {
514  throw_parse_error(m_path, line, token, lineNumber);
515  }
516 
517  while (lineStream >> token)
518  {
519  m_layerNames.push_back(token);
520  }
521  }
522  else if (line.find(PropertyRecord::RECORD_TOKEN) == 0)
523  {
524  // component property record line
525  std::string token;
526  if (!(lineStream >> token))
527  {
528  throw_parse_error(m_path, line, token, lineNumber);
529  }
530 
531  if (token != PropertyRecord::RECORD_TOKEN)
532  {
533  throw_parse_error(m_path, line, token, lineNumber);
534  }
535 
536  auto pPropertyRecord = std::make_shared<PropertyRecord>();
537 
538  if (!(lineStream >> pPropertyRecord->name))
539  {
540  throw_parse_error(m_path, line, token, lineNumber);
541  }
542 
543  if (!(lineStream >> token))
544  {
545  throw_parse_error(m_path, line, token, lineNumber);
546  }
547 
548  if (!token.empty())
549  {
550  // handle case where the beginning of the value, after the opening single-quote, is whitespace...
551  if (token == "'")
552  {
553  // eat the single-quote and get the next token, i.e. the actual value (w/ space in front)
554  if (!(lineStream >> token))
555  {
556  throw_parse_error(m_path, line, token, lineNumber);
557  }
558  }
559 
560  if (token.size() > 0 && token[0] == '\'')
561  {
562  // remove leading quote
563  token.erase(0, 1);
564  }
565 
566  if (token.size() > 0 && token[token.size() - 1] == '\'')
567  {
568  // remove trailing quote
569  token.erase(token.size() - 1);
570  }
571 
572  Utils::str_trim(token);
573  }
574 
575  pPropertyRecord->value = token;
576 
577  double f;
578  while (lineStream >> f)
579  {
580  pPropertyRecord->floatValues.push_back(f);
581  }
582 
583  if (pCurrentNetRecord != nullptr)
584  {
585  pCurrentNetRecord->m_propertyRecords.push_back(pPropertyRecord);
586  }
587  else if (pCurrentPackageRecord != nullptr)
588  {
589  pCurrentPackageRecord->m_propertyRecords.push_back(pPropertyRecord);
590  }
591  else if (pCurrentFeatureGroupRecord != nullptr)
592  {
593  pCurrentFeatureGroupRecord->m_propertyRecords.push_back(pPropertyRecord);
594  }
595  else
596  {
597  // top-level PRP record
598  m_propertyRecords.push_back(pPropertyRecord);
599  }
600  }
601  else if (line.find(NET_RECORD_TOKEN) == 0)
602  {
603  // net record line
604  std::string token;
605 
606  if (!(lineStream >> token))
607  {
608  throw_parse_error(m_path, line, token, lineNumber);
609  }
610 
611  if (token != NET_RECORD_TOKEN)
612  {
613  throw_parse_error(m_path, line, token, lineNumber);
614  }
615 
616  // finish current (previous) net record
617  if (pCurrentNetRecord != nullptr)
618  {
619  // finish up (any) current subnet record
620  if (pCurrentSubnetRecord != nullptr)
621  {
622  pCurrentNetRecord->m_subnetRecords.push_back(pCurrentSubnetRecord);
623  pCurrentSubnetRecord.reset();
624  }
625 
626  m_netRecords.push_back(pCurrentNetRecord);
627  //m_netRecordsByName[pCurrentNetRecord->name] = pCurrentNetRecord;
628  pCurrentNetRecord.reset();
629  }
630 
631  // net name
632  if (!std::getline(lineStream, token, ';'))
633  {
634  throw_parse_error(m_path, line, token, lineNumber);
635  }
636 
637  // trim whitespace from beginning
638  str_trim(token);
639 
640  // create new net record
641  pCurrentNetRecord = std::make_shared<NetRecord>();
642  pCurrentNetRecord->name = token;
643  pCurrentNetRecord->index = static_cast<unsigned>(m_netRecords.size());
644 
645  std::string attrIdString;
646  lineStream >> attrIdString;
647  if (!pCurrentNetRecord->ParseAttributeLookupTable(attrIdString))
648  {
649  throw_parse_error(m_path, line, token, lineNumber);
650  }
651  }
652  else if (line.find(NetRecord::SubnetRecord::RECORD_TOKEN) == 0)
653  {
654  // component record line
655  std::string token;
656 
657  lineStream >> token;
658  if (token != NetRecord::SubnetRecord::RECORD_TOKEN)
659  {
660  throw_parse_error(m_path, line, token, lineNumber);
661  }
662 
663  if (pCurrentNetRecord != nullptr)
664  {
665  // finish current (previous) subnet record
666  if (pCurrentSubnetRecord != nullptr)
667  {
668  pCurrentNetRecord->m_subnetRecords.push_back(pCurrentSubnetRecord);
669  pCurrentSubnetRecord.reset();
670  }
671  }
672 
673  pCurrentSubnetRecord = std::make_shared<NetRecord::SubnetRecord>();
674  pCurrentSubnetRecord->index = static_cast<unsigned>(pCurrentNetRecord->m_subnetRecords.size());
675 
676  // subnet type
677  lineStream >> token;
678  if (token == NetRecord::SubnetRecord::RECORD_TYPE_VIA_TOKEN)
679  {
680  pCurrentSubnetRecord->type = NetRecord::SubnetRecord::Type::Via;
681  }
682  else if (token == NetRecord::SubnetRecord::RECORD_TYPE_TRACE_TOKEN)
683  {
684  pCurrentSubnetRecord->type = NetRecord::SubnetRecord::Type::Trace;
685  }
686  else if (token == NetRecord::SubnetRecord::RECORD_TYPE_PLANE_TOKEN)
687  {
688  pCurrentSubnetRecord->type = NetRecord::SubnetRecord::Type::Plane;
689 
690  // fill type
691  lineStream >> token;
692  if (token == "S")
693  {
694  pCurrentSubnetRecord->fillType = NetRecord::SubnetRecord::FillType::Solid;
695  }
696  else if (token == "O")
697  {
698  pCurrentSubnetRecord->fillType = NetRecord::SubnetRecord::FillType::Outline;
699  }
700  else
701  {
702  throw_parse_error(m_path, line, token, lineNumber);
703  }
704 
705  // cutout type
706  lineStream >> token;
707  if (token == "C")
708  {
709  pCurrentSubnetRecord->cutoutType = NetRecord::SubnetRecord::CutoutType::Circle;
710  }
711  else if (token == "R")
712  {
713  pCurrentSubnetRecord->cutoutType = NetRecord::SubnetRecord::CutoutType::Rectangle;
714  }
715  else if (token == "O")
716  {
717  pCurrentSubnetRecord->cutoutType = NetRecord::SubnetRecord::CutoutType::Octagon;
718  }
719  else if (token == "E")
720  {
721  pCurrentSubnetRecord->cutoutType = NetRecord::SubnetRecord::CutoutType::Exact;
722  }
723  else
724  {
725  throw_parse_error(m_path, line, token, lineNumber);
726  }
727 
728  // fill size
729  lineStream >> pCurrentSubnetRecord->fillSize;
730  }
731  else if (token == NetRecord::SubnetRecord::RECORD_TYPE_TOEPRINT_TOKEN)
732  {
733  pCurrentSubnetRecord->type = NetRecord::SubnetRecord::Type::Toeprint;
734 
735  // side
736  lineStream >> token;
737  if (token == "T")
738  {
739  pCurrentSubnetRecord->side = BoardSide::Top;
740  }
741  else if (token == "B")
742  {
743  pCurrentSubnetRecord->side = BoardSide::Bottom;
744  }
745  else
746  {
747  throw_parse_error(m_path, line, token, lineNumber);
748  }
749 
750  // componentNumber
751  lineStream >> pCurrentSubnetRecord->componentNumber;
752 
753  // toeprintNumber
754  lineStream >> pCurrentSubnetRecord->toeprintNumber;
755  }
756  else
757  {
758  throw_parse_error(m_path, line, token, lineNumber);
759  }
760  }
761  else if (line.find(FEATURE_ID_RECORD_TOKEN) == 0)
762  {
763  // component record line
764  std::string token;
765 
766  if (!(lineStream >> token) || token != FEATURE_ID_RECORD_TOKEN)
767  {
768  throw_parse_error(m_path, line, token, lineNumber);
769  }
770 
771  auto pFeatureIdRecord = std::make_shared<FeatureIdRecord>();
772 
773  // type
774  if (!(lineStream >> token))
775  {
776  throw_parse_error(m_path, line, token, lineNumber);
777  }
778 
779  if (token == "C")
780  {
781  pFeatureIdRecord->type = FeatureIdRecord::Type::Copper;
782  }
783  else if (token == "L")
784  {
785  pFeatureIdRecord->type = FeatureIdRecord::Type::Laminate;
786  }
787  else if (token == "H")
788  {
789  pFeatureIdRecord->type = FeatureIdRecord::Type::Hole;
790  }
791  else
792  {
793  throw_parse_error(m_path, line, token, lineNumber);
794  }
795 
796  // layer number
797  if (!(lineStream >> pFeatureIdRecord->layerNumber))
798  {
799  throw_parse_error(m_path, line, token, lineNumber);
800  }
801 
802  // feature number
803  if (!(lineStream >> pFeatureIdRecord->featureNumber))
804  {
805  throw_parse_error(m_path, line, token, lineNumber);
806  }
807 
808  if (pCurrentNetRecord != nullptr &&
809  pCurrentSubnetRecord != nullptr)
810  {
811  pCurrentSubnetRecord->m_featureIdRecords.push_back(pFeatureIdRecord);
812  }
813  else if (pCurrentFeatureGroupRecord != nullptr)
814  {
815  pCurrentFeatureGroupRecord->m_featureIdRecords.push_back(pFeatureIdRecord);
816  }
817  else
818  {
819  throw_parse_error(m_path, line, token, lineNumber);
820  }
821  }
822  else if (line.find(PACKAGE_RECORD_TOKEN) == 0)
823  {
824  // package record line
825  std::string token;
826 
827  if (!(lineStream >> token) || token != PACKAGE_RECORD_TOKEN)
828  {
829  throw_parse_error(m_path, line, token, lineNumber);
830  }
831 
832  // finish current (previous) net record
833  if (pCurrentNetRecord != nullptr)
834  {
835  // finish up (any) current subnet record
836  if (pCurrentSubnetRecord != nullptr)
837  {
838  pCurrentNetRecord->m_subnetRecords.push_back(pCurrentSubnetRecord);
839  pCurrentSubnetRecord.reset();
840  }
841 
842  m_netRecords.push_back(pCurrentNetRecord);
843  //m_netRecordsByName[pCurrentNetRecord->name] = pCurrentNetRecord;
844  pCurrentNetRecord.reset();
845  }
846  else if (pCurrentPackageRecord != nullptr)
847  {
848  // finish up any current (previous) pin records
849  if (pCurrentPinRecord != nullptr)
850  {
851  // check for overflow
852  if (pCurrentPackageRecord->m_pinRecords.size() > UINT_MAX)
853  {
854  throw_parse_error(m_path, line, token, lineNumber);
855  }
856  pCurrentPinRecord->index = static_cast<unsigned>(pCurrentPackageRecord->m_pinRecords.size());
857  pCurrentPackageRecord->m_pinRecords.push_back(pCurrentPinRecord);
858  pCurrentPinRecord.reset();
859  }
860 
861  // finish up any current (previous) package records
862  m_packageRecords.push_back(pCurrentPackageRecord);
863  //m_packageRecordsByName[pCurrentPackageRecord->name] = pCurrentPackageRecord;
864  pCurrentPackageRecord.reset();
865  }
866 
867  pCurrentPackageRecord = std::make_shared<PackageRecord>();
868  pCurrentPackageRecord->index = static_cast<unsigned int>(m_packageRecords.size());
869 
870  // name
871  if (!(lineStream >> pCurrentPackageRecord->name))
872  {
873  throw_parse_error(m_path, line, token, lineNumber);
874  }
875 
876  // pitch
877  if (!(lineStream >> pCurrentPackageRecord->pitch))
878  {
879  throw_parse_error(m_path, line, token, lineNumber);
880  }
881 
882  // xmin, ymin
883  if (!(lineStream >> pCurrentPackageRecord->xMin >> pCurrentPackageRecord->yMin))
884  {
885  throw_parse_error(m_path, line, token, lineNumber);
886  }
887 
888  // xmax
889  if (!(lineStream >> pCurrentPackageRecord->xMax))
890  {
891  throw_parse_error(m_path, line, token, lineNumber);
892  }
893 
894  // ymax and attributes/ID string
895  if (!std::getline(lineStream, token, ';'))
896  {
897  throw_parse_error(m_path, line, token, lineNumber);
898  }
899 
900  // ymax
901  pCurrentPackageRecord->yMax = std::stof(token);
902 
903  std::string attrIdString;
904  lineStream >> attrIdString;
905  if (!pCurrentPackageRecord->ParseAttributeLookupTable(attrIdString))
906  {
907  throw_parse_error(m_path, line, token, lineNumber);
908  }
909  }
910  else if (line.find(PIN_RECORD_TOKEN) == 0)
911  {
912  // package pin record line
913  std::string token;
914 
915  if (!(lineStream >> token) || token != PIN_RECORD_TOKEN)
916  {
917  throw_parse_error(m_path, line, token, lineNumber);
918  }
919 
920  if (pCurrentPackageRecord != nullptr)
921  {
922  // finish up any current (previous) pin records
923  if (pCurrentPinRecord != nullptr)
924  {
925  // check for overflow
926  if (pCurrentPackageRecord->m_pinRecords.size() > UINT_MAX)
927  {
928  throw_parse_error(m_path, line, token, lineNumber);
929  }
930  pCurrentPinRecord->index = static_cast<unsigned>(pCurrentPackageRecord->m_pinRecords.size());
931  pCurrentPackageRecord->m_pinRecords.push_back(pCurrentPinRecord);
932  pCurrentPinRecord.reset();
933  }
934  }
935 
936  pCurrentPinRecord = std::make_shared<PackageRecord::PinRecord>();
937  //pCurrentPinRecord->index = static_cast<unsigned>(pCurrentPackageRecord->m_pinRecords.size());
938 
939  // name
940  lineStream >> pCurrentPinRecord->name;
941 
942  // type
943  if (!(lineStream >> token))
944  {
945  throw_parse_error(m_path, line, token, lineNumber);
946  }
947 
948  if (token == "T")
949  {
950  pCurrentPinRecord->type = PackageRecord::PinRecord::Type::ThroughHole;
951  }
952  else if (token == "B")
953  {
954  pCurrentPinRecord->type = PackageRecord::PinRecord::Type::Blind;
955  }
956  else if (token == "S")
957  {
958  pCurrentPinRecord->type = PackageRecord::PinRecord::Type::Surface;
959  }
960  else
961  {
962  logerror("unknown pin type: [" + token + "]");
963  throw_parse_error(m_path, line, token, lineNumber);
964  }
965 
966  // xc, xy
967  if (!(lineStream >> pCurrentPinRecord->xCenter >> pCurrentPinRecord->yCenter))
968  {
969  throw_parse_error(m_path, line, token, lineNumber);
970  }
971 
972  // finished hole size (fhs)
973  if (!(lineStream >> pCurrentPinRecord->finishedHoleSize))
974  {
975  throw_parse_error(m_path, line, token, lineNumber);
976  }
977 
978  // electrical type
979  if (!(lineStream >> token))
980  {
981  throw_parse_error(m_path, line, token, lineNumber);
982  }
983 
984  if (token == "E")
985  {
986  pCurrentPinRecord->electricalType = PackageRecord::PinRecord::ElectricalType::Electrical;
987  }
988  else if (token == "M")
989  {
990  pCurrentPinRecord->electricalType = PackageRecord::PinRecord::ElectricalType::NonElectrical;
991  }
992  else if (token == "U")
993  {
994  pCurrentPinRecord->electricalType = PackageRecord::PinRecord::ElectricalType::Undefined;
995  }
996  else
997  {
998  logerror("unknown pin electrical type: [" + token + "]");
999  throw_parse_error(m_path, line, token, lineNumber);
1000  }
1001 
1002  // mount type
1003  if (!(lineStream >> token))
1004  {
1005  throw_parse_error(m_path, line, token, lineNumber);
1006  }
1007 
1008  if (token == "S")
1009  {
1010  pCurrentPinRecord->mountType = PackageRecord::PinRecord::MountType::Smt;
1011  }
1012  else if (token == "D")
1013  {
1014  pCurrentPinRecord->mountType = PackageRecord::PinRecord::MountType::RecommendedSmtPad;
1015  }
1016  else if (token == "T")
1017  {
1018  pCurrentPinRecord->mountType = PackageRecord::PinRecord::MountType::MT_ThroughHole;
1019  }
1020  else if (token == "R")
1021  {
1022  pCurrentPinRecord->mountType = PackageRecord::PinRecord::MountType::RecommendedThroughHole;
1023  }
1024  else if (token == "P")
1025  {
1026  pCurrentPinRecord->mountType = PackageRecord::PinRecord::MountType::Pressfit;
1027  }
1028  else if (token == "N")
1029  {
1030  pCurrentPinRecord->mountType = PackageRecord::PinRecord::MountType::NonBoard;
1031  }
1032  else if (token == "H")
1033  {
1034  pCurrentPinRecord->mountType = PackageRecord::PinRecord::MountType::Hole;
1035  }
1036  else if (token == "U")
1037  {
1038  pCurrentPinRecord->mountType = PackageRecord::PinRecord::MountType::MT_Undefined;
1039  }
1040  else
1041  {
1042  logerror("unknown pin mount type: [" + token + "]");
1043  throw_parse_error(m_path, line, token, lineNumber);
1044  }
1045 
1046  // ID=<id>
1047  // PIN record, ID field is optional: spec pg.31
1048  if (lineStream >> token)
1049  {
1050  std::stringstream idStream(token);
1051  if (!std::getline(idStream, token, '=') || token != "ID")
1052  {
1053  throw_parse_error(m_path, line, token, lineNumber);
1054  }
1055 
1056  idStream >> pCurrentPinRecord->id;
1057  }
1058  else
1059  {
1060 
1061 #define SIMULATE_PARSE_ERROR 0
1062 #if SIMULATE_PARSE_ERROR
1063  throw_parse_error(m_path, line, token, lineNumber);
1064 #endif
1065 
1066  pCurrentPinRecord->id = UINT_MAX;
1067  }
1068  }
1069  else if (line.find(FEATURE_GROUP_RECORD_TOKEN) == 0)
1070  {
1071  // feature group record line
1072  std::string token;
1073 
1074  if (!(lineStream >> token))
1075  {
1076  throw_parse_error(m_path, line, token, lineNumber);
1077  }
1078 
1079  if (token != FEATURE_GROUP_RECORD_TOKEN)
1080  {
1081  throw_parse_error(m_path, line, token, lineNumber);
1082  }
1083 
1084  // finish current (previous) net record
1085  if (pCurrentNetRecord != nullptr)
1086  {
1087  // finish up (any) current subnet record
1088  if (pCurrentSubnetRecord != nullptr)
1089  {
1090  pCurrentNetRecord->m_subnetRecords.push_back(pCurrentSubnetRecord);
1091  pCurrentSubnetRecord.reset();
1092  }
1093 
1094  m_netRecords.push_back(pCurrentNetRecord);
1095  //m_netRecordsByName[pCurrentNetRecord->name] = pCurrentNetRecord;
1096  pCurrentNetRecord.reset();
1097  }
1098  else if (pCurrentPackageRecord != nullptr)
1099  {
1100  // finish up any current (previous) pin records
1101  if (pCurrentPinRecord != nullptr)
1102  {
1103  // check for overflow
1104  if (pCurrentPackageRecord->m_pinRecords.size() > UINT_MAX)
1105  {
1106  throw_parse_error(m_path, line, token, lineNumber);
1107  }
1108  pCurrentPinRecord->index = static_cast<unsigned>(pCurrentPackageRecord->m_pinRecords.size());
1109  pCurrentPackageRecord->m_pinRecords.push_back(pCurrentPinRecord);
1110  pCurrentPinRecord.reset();
1111  }
1112 
1113  // finish up any current (previous) package records
1114  m_packageRecords.push_back(pCurrentPackageRecord);
1115  //m_packageRecordsByName[pCurrentPackageRecord->name] = pCurrentPackageRecord;
1116  pCurrentPackageRecord.reset();
1117  }
1118  else if (pCurrentFeatureGroupRecord != nullptr)
1119  {
1120  m_featureGroupRecords.push_back(pCurrentFeatureGroupRecord);
1121  pCurrentFeatureGroupRecord.reset();
1122  }
1123 
1124  pCurrentFeatureGroupRecord = std::make_shared<FeatureGroupRecord>();
1125 
1126  if (!(lineStream >> pCurrentFeatureGroupRecord->type))
1127  {
1128  throw_parse_error(m_path, line, token, lineNumber);
1129  }
1130  }
1131  else if (line.find(PackageRecord::OutlineRecord::RECTANGLE_RECORD_TOKEN) == 0)
1132  {
1133  std::string token;
1134  if (!(lineStream >> token))
1135  {
1136  throw_parse_error(m_path, line, token, lineNumber);
1137  }
1138 
1139  if (token != PackageRecord::OutlineRecord::RECTANGLE_RECORD_TOKEN)
1140  {
1141  throw_parse_error(m_path, line, token, lineNumber);
1142  }
1143 
1144  auto pOutlineRecord = std::make_shared<PackageRecord::OutlineRecord>();
1145  pOutlineRecord->type = PackageRecord::OutlineRecord::Type::Rectangle;
1146 
1147  if (!(lineStream >> pOutlineRecord->lowerLeftX))
1148  {
1149  throw_parse_error(m_path, line, token, lineNumber);
1150  }
1151 
1152  if (!(lineStream >> pOutlineRecord->lowerLeftY))
1153  {
1154  throw_parse_error(m_path, line, token, lineNumber);
1155  }
1156 
1157  if (!(lineStream >> pOutlineRecord->width))
1158  {
1159  throw_parse_error(m_path, line, token, lineNumber);
1160  }
1161 
1162  if (!(lineStream >> pOutlineRecord->height))
1163  {
1164  throw_parse_error(m_path, line, token, lineNumber);
1165  }
1166 
1167  if (pCurrentPackageRecord != nullptr)
1168  {
1169  if (pCurrentPinRecord != nullptr)
1170  {
1171  pCurrentPinRecord->m_outlineRecords.push_back(pOutlineRecord);
1172  }
1173  else
1174  {
1175  pCurrentPackageRecord->m_outlineRecords.push_back(pOutlineRecord);
1176  }
1177  }
1178  else
1179  {
1180  throw_parse_error(m_path, line, token, lineNumber);
1181  }
1182  }
1183  else if (line.find(PackageRecord::OutlineRecord::CIRCLE_RECORD_TOKEN) == 0)
1184  {
1185  std::string token;
1186  if (!(lineStream >> token))
1187  {
1188  throw_parse_error(m_path, line, token, lineNumber);
1189  }
1190 
1191  if (token != PackageRecord::OutlineRecord::CIRCLE_RECORD_TOKEN)
1192  {
1193  throw_parse_error(m_path, line, token, lineNumber);
1194  }
1195 
1196  auto pOutlineRecord = std::make_shared<PackageRecord::OutlineRecord>();
1197  pOutlineRecord->type = PackageRecord::OutlineRecord::Type::Circle;
1198 
1199  if (!(lineStream >> pOutlineRecord->xCenter))
1200  {
1201  throw_parse_error(m_path, line, token, lineNumber);
1202  }
1203 
1204  if (!(lineStream >> pOutlineRecord->yCenter))
1205  {
1206  throw_parse_error(m_path, line, token, lineNumber);
1207  }
1208 
1209  if (!(lineStream >> pOutlineRecord->radius))
1210  {
1211  throw_parse_error(m_path, line, token, lineNumber);
1212  }
1213 
1214  if (pCurrentPackageRecord != nullptr)
1215  {
1216  if (pCurrentPinRecord != nullptr)
1217  {
1218  pCurrentPinRecord->m_outlineRecords.push_back(pOutlineRecord);
1219  }
1220  else
1221  {
1222  pCurrentPackageRecord->m_outlineRecords.push_back(pOutlineRecord);
1223  }
1224  }
1225  else
1226  {
1227  throw_parse_error(m_path, line, token, lineNumber);
1228  }
1229  }
1230  else if (line.find(PackageRecord::OutlineRecord::SQUARE_RECORD_TOKEN) == 0)
1231  {
1232  std::string token;
1233  if (!(lineStream >> token))
1234  {
1235  throw_parse_error(m_path, line, token, lineNumber);
1236  }
1237 
1238  if (token != PackageRecord::OutlineRecord::SQUARE_RECORD_TOKEN)
1239  {
1240  throw_parse_error(m_path, line, token, lineNumber);
1241  }
1242 
1243  auto pOutlineRecord = std::make_shared<PackageRecord::OutlineRecord>();
1244  pOutlineRecord->type = PackageRecord::OutlineRecord::Type::Square;
1245 
1246  if (!(lineStream >> pOutlineRecord->xCenter))
1247  {
1248  throw_parse_error(m_path, line, token, lineNumber);
1249  }
1250 
1251  if (!(lineStream >> pOutlineRecord->yCenter))
1252  {
1253  throw_parse_error(m_path, line, token, lineNumber);
1254  }
1255 
1256  if (!(lineStream >> pOutlineRecord->halfSide))
1257  {
1258  throw_parse_error(m_path, line, token, lineNumber);
1259  }
1260 
1261  if (pCurrentPackageRecord != nullptr)
1262  {
1263  if (pCurrentPinRecord != nullptr)
1264  {
1265  pCurrentPinRecord->m_outlineRecords.push_back(pOutlineRecord);
1266  }
1267  else
1268  {
1269  pCurrentPackageRecord->m_outlineRecords.push_back(pOutlineRecord);
1270  }
1271  }
1272  else
1273  {
1274  throw_parse_error(m_path, line, token, lineNumber);
1275  }
1276  }
1277  else if (line.find(PackageRecord::OutlineRecord::CONTOUR_BEGIN_RECORD_TOKEN) == 0)
1278  {
1279  std::string token;
1280  if (!(lineStream >> token))
1281  {
1282  throw_parse_error(m_path, line, token, lineNumber);
1283  }
1284 
1285  if (token != PackageRecord::OutlineRecord::CONTOUR_BEGIN_RECORD_TOKEN)
1286  {
1287  throw_parse_error(m_path, line, token, lineNumber);
1288  }
1289 
1290  pCurrentContourOutlineRecord = std::make_shared<PackageRecord::OutlineRecord>();
1291  pCurrentContourOutlineRecord->type = PackageRecord::OutlineRecord::Type::Contour;
1292  }
1293  else if (line.find(PackageRecord::OutlineRecord::CONTOUR_END_RECORD_TOKEN) == 0)
1294  {
1295  std::string token;
1296  if (!(lineStream >> token))
1297  {
1298  throw_parse_error(m_path, line, token, lineNumber);
1299  }
1300 
1301  if (token != PackageRecord::OutlineRecord::CONTOUR_END_RECORD_TOKEN)
1302  {
1303  throw_parse_error(m_path, line, token, lineNumber);
1304  }
1305 
1306  if (pCurrentPackageRecord != nullptr)
1307  {
1308  if (pCurrentPinRecord != nullptr)
1309  {
1310  pCurrentPinRecord->m_outlineRecords.push_back(pCurrentContourOutlineRecord);
1311  }
1312  else
1313  {
1314  pCurrentPackageRecord->m_outlineRecords.push_back(pCurrentContourOutlineRecord);
1315  }
1316  }
1317  else
1318  {
1319  throw_parse_error(m_path, line, token, lineNumber);
1320  }
1321 
1322  pCurrentContourOutlineRecord.reset();
1323  }
1324  else if (line.find(ContourPolygon::BEGIN_RECORD_TOKEN) == 0)
1325  {
1326  std::string token;
1327  if (!(lineStream >> token))
1328  {
1329  throw_parse_error(m_path, line, token, lineNumber);
1330  }
1331 
1332  if (token != ContourPolygon::BEGIN_RECORD_TOKEN)
1333  {
1334  throw_parse_error(m_path, line, token, lineNumber);
1335  }
1336 
1337  pCurrentContourPolygon = std::make_shared<ContourPolygon>();
1338 
1339  if (!(lineStream >> pCurrentContourPolygon->xStart))
1340  {
1341  throw_parse_error(m_path, line, token, lineNumber);
1342  }
1343 
1344  if (!(lineStream >> pCurrentContourPolygon->yStart))
1345  {
1346  throw_parse_error(m_path, line, token, lineNumber);
1347  }
1348 
1349  if (!(lineStream >> token))
1350  {
1351  throw_parse_error(m_path, line, token, lineNumber);
1352  }
1353 
1354  if (token == ContourPolygon::ISLAND_TYPE_TOKEN)
1355  {
1356  pCurrentContourPolygon->type = ContourPolygon::Type::Island;
1357  }
1358  else if (token == ContourPolygon::HOLE_TYPE_TOKEN)
1359  {
1360  pCurrentContourPolygon->type = ContourPolygon::Type::Hole;
1361  }
1362  else
1363  {
1364  throw_parse_error(m_path, line, token, lineNumber);
1365  }
1366  }
1367  else if (line.find(ContourPolygon::END_RECORD_TOKEN) == 0)
1368  {
1369  std::string token;
1370  if (!(lineStream >> token))
1371  {
1372  throw_parse_error(m_path, line, token, lineNumber);
1373  }
1374 
1375  if (token != ContourPolygon::END_RECORD_TOKEN)
1376  {
1377  throw_parse_error(m_path, line, token, lineNumber);
1378  }
1379 
1380  if (pCurrentContourOutlineRecord != nullptr)
1381  {
1382  pCurrentContourOutlineRecord->m_contourPolygons.push_back(pCurrentContourPolygon);
1383  pCurrentContourPolygon.reset();
1384  }
1385  else
1386  {
1387  throw_parse_error(m_path, line, token, lineNumber);
1388  }
1389  }
1390  else if (line.find(ContourPolygon::PolygonPart::ARC_RECORD_TOKEN) == 0)
1391  {
1392  std::string token;
1393  if (!(lineStream >> token))
1394  {
1395  throw_parse_error(m_path, line, token, lineNumber);
1396  }
1397 
1398  if (token != ContourPolygon::PolygonPart::ARC_RECORD_TOKEN)
1399  {
1400  throw_parse_error(m_path, line, token, lineNumber);
1401  }
1402 
1403  auto pPolygonPart = std::make_shared<ContourPolygon::PolygonPart>();
1404  pPolygonPart->type = ContourPolygon::PolygonPart::Type::Arc;
1405 
1406  if (!(lineStream >> pPolygonPart->endX))
1407  {
1408  throw_parse_error(m_path, line, token, lineNumber);
1409  }
1410 
1411  if (!(lineStream >> pPolygonPart->endY))
1412  {
1413  throw_parse_error(m_path, line, token, lineNumber);
1414  }
1415 
1416  if (!(lineStream >> pPolygonPart->xCenter))
1417  {
1418  throw_parse_error(m_path, line, token, lineNumber);
1419  }
1420 
1421  if (!(lineStream >> pPolygonPart->yCenter))
1422  {
1423  throw_parse_error(m_path, line, token, lineNumber);
1424  }
1425 
1426  if (!(lineStream >> token))
1427  {
1428  throw_parse_error(m_path, line, token, lineNumber);
1429  }
1430 
1431  if (token == "y" || token == "Y")
1432  {
1433  pPolygonPart->isClockwise = true;
1434  }
1435  else if (token == "n" || token == "N")
1436  {
1437  pPolygonPart->isClockwise = false;
1438  }
1439  else
1440  {
1441  throw_parse_error(m_path, line, token, lineNumber);
1442  }
1443 
1444  if (pCurrentContourPolygon != nullptr)
1445  {
1446  pCurrentContourPolygon->m_polygonParts.push_back(pPolygonPart);
1447  }
1448  else
1449  {
1450  throw_parse_error(m_path, line, token, lineNumber);
1451  }
1452  }
1453  else if (line.find(ContourPolygon::PolygonPart::SEGMENT_RECORD_TOKEN) == 0)
1454  {
1455  std::string token;
1456  if (!(lineStream >> token))
1457  {
1458  throw_parse_error(m_path, line, token, lineNumber);
1459  }
1460 
1461  if (token != ContourPolygon::PolygonPart::SEGMENT_RECORD_TOKEN)
1462  {
1463  throw_parse_error(m_path, line, token, lineNumber);
1464  }
1465 
1466  auto pPolygonPart = std::make_shared<ContourPolygon::PolygonPart>();
1467  pPolygonPart->type = ContourPolygon::PolygonPart::Type::Segment;
1468 
1469  if (!(lineStream >> pPolygonPart->endX))
1470  {
1471  throw_parse_error(m_path, line, token, lineNumber);
1472  }
1473 
1474  if (!(lineStream >> pPolygonPart->endY))
1475  {
1476  throw_parse_error(m_path, line, token, lineNumber);
1477  }
1478 
1479  if (pCurrentContourPolygon != nullptr)
1480  {
1481  pCurrentContourPolygon->m_polygonParts.push_back(pPolygonPart);
1482  }
1483  else
1484  {
1485  throw_parse_error(m_path, line, token, lineNumber);
1486  }
1487  }
1488  else
1489  {
1490  // unrecognized record line
1491  parse_info pi(m_path, line, lineNumber);
1492  logwarn(pi.toString("unrecognized record line in EDADATA file:"));
1493  }
1494  }
1495  else
1496  {
1497  // empty line
1498  continue;
1499  }
1500  }
1501 
1502  // finish current (previous) net record
1503  // this is the case where the last line of the file is not a net record (and there are no PKG records)
1504  if (pCurrentNetRecord != nullptr)
1505  {
1506  // finish current (previous) subnet record
1507  // case where we reach the end of the file before a new net record is encountered (and there are no PKG records)
1508  if (pCurrentSubnetRecord != nullptr)
1509  {
1510  pCurrentNetRecord->m_subnetRecords.push_back(pCurrentSubnetRecord);
1511  pCurrentSubnetRecord.reset();
1512  }
1513 
1514  m_netRecords.push_back(pCurrentNetRecord);
1515  //m_netRecordsByName[pCurrentNetRecord->name] = pCurrentNetRecord;
1516  pCurrentNetRecord.reset();
1517  }
1518  else if (pCurrentPackageRecord != nullptr)
1519  {
1520  // finish up any current (previous) pin records
1521  if (pCurrentPinRecord != nullptr)
1522  {
1523  // check for overflow
1524  if (pCurrentPackageRecord->m_pinRecords.size() > UINT_MAX)
1525  {
1526  throw_parse_error(m_path, line, "", lineNumber);
1527  }
1528  pCurrentPinRecord->index = static_cast<unsigned>(pCurrentPackageRecord->m_pinRecords.size());
1529  pCurrentPackageRecord->m_pinRecords.push_back(pCurrentPinRecord);
1530  pCurrentPinRecord.reset();
1531  }
1532 
1533  // finish up any current (previous) package records
1534  m_packageRecords.push_back(pCurrentPackageRecord);
1535  //m_packageRecordsByName[pCurrentPackageRecord->name] = pCurrentPackageRecord;
1536  pCurrentPackageRecord.reset();
1537  }
1538  else if (pCurrentFeatureGroupRecord != nullptr)
1539  {
1540  m_featureGroupRecords.push_back(pCurrentFeatureGroupRecord);
1541  pCurrentFeatureGroupRecord.reset();
1542  }
1543 
1544  edaDataFile.close();
1545  }
1546  catch (parse_error& pe)
1547  {
1548  auto m = pe.toString("Parse Error:");
1549  logerror(m);
1550  // cleanup file
1551  edaDataFile.close();
1552  throw pe;
1553  }
1554  catch (std::exception& e)
1555  {
1556  parse_info pi(m_path, line, lineNumber);
1557  const auto m = pi.toString();
1558  logexception_msg(e, m);
1559  // cleanup file
1560  edaDataFile.close();
1561  throw e;
1562  }
1563 
1564  return true;
1565  }
1566 
1567  // Inherited via IProtoBuffable
1568  std::unique_ptr<Odb::Lib::Protobuf::EdaDataFile::PackageRecord>
1569  EdaDataFile::PackageRecord::to_protobuf() const
1570  {
1571  std::unique_ptr<Odb::Lib::Protobuf::EdaDataFile::PackageRecord> pPackageRecordMessage(new Odb::Lib::Protobuf::EdaDataFile::PackageRecord);
1572  pPackageRecordMessage->set_name(name);
1573  pPackageRecordMessage->set_pitch(pitch);
1574  pPackageRecordMessage->set_xmin(xMin);
1575  pPackageRecordMessage->set_ymin(yMin);
1576  pPackageRecordMessage->set_xmax(xMax);
1577  pPackageRecordMessage->set_ymax(yMax);
1578  //pPackageRecordMessage->set_attributesidstring(attributesIdString);
1579  for (const auto& pinRecord : m_pinRecords)
1580  {
1581  auto pPinRecordMessage = pPackageRecordMessage->add_pinrecords();
1582  pPinRecordMessage->CopyFrom(*pinRecord->to_protobuf());
1583  }
1584  for (const auto& kvPinRecord : m_pinRecordsByName)
1585  {
1586  (*pPackageRecordMessage->mutable_pinrecordsbyname())[kvPinRecord.first] = *kvPinRecord.second->to_protobuf();
1587  }
1588  for (const auto& propertyRecord : m_propertyRecords)
1589  {
1590  auto pPropertyRecordMessage = pPackageRecordMessage->add_propertyrecords();
1591  pPropertyRecordMessage->CopyFrom(*propertyRecord->to_protobuf());
1592  }
1593  for (const auto& outlineRecord : m_outlineRecords)
1594  {
1595  auto pOutlineRecordMessage = pPackageRecordMessage->add_outlinerecords();
1596  pOutlineRecordMessage->CopyFrom(*outlineRecord->to_protobuf());
1597  }
1598  for (const auto& kvAttributeAssignment : m_attributeLookupTable)
1599  {
1600  (*pPackageRecordMessage->mutable_attributelookuptable())[kvAttributeAssignment.first] = kvAttributeAssignment.second;
1601  }
1602  return pPackageRecordMessage;
1603  }
1604 
1605  void EdaDataFile::PackageRecord::from_protobuf(const Odb::Lib::Protobuf::EdaDataFile::PackageRecord& message)
1606  {
1607  name = message.name();
1608  pitch = message.pitch();
1609  xMin = message.xmin();
1610  yMin = message.ymin();
1611  xMax = message.xmax();
1612  yMax = message.ymax();
1613  //attributesIdString = message.attributesidstring();
1614 
1615  for (const auto& pinRecordMessage : message.pinrecords())
1616  {
1617  auto pPinRecord = std::make_shared<PinRecord>();
1618  pPinRecord->from_protobuf(pinRecordMessage);
1619  m_pinRecords.push_back(pPinRecord);
1620  }
1621 
1622  for (const auto& kvPinRecordMessage : message.pinrecordsbyname())
1623  {
1624  auto pPinRecord = std::make_shared<PinRecord>();
1625  pPinRecord->from_protobuf(kvPinRecordMessage.second);
1626  m_pinRecordsByName[kvPinRecordMessage.first] = pPinRecord;
1627  }
1628 
1629  for (const auto& propertyRecordMessage : message.propertyrecords())
1630  {
1631  auto pPropertyRecord = std::make_shared<PropertyRecord>();
1632  pPropertyRecord->from_protobuf(propertyRecordMessage);
1633  m_propertyRecords.push_back(pPropertyRecord);
1634  }
1635 
1636  for (const auto& outlineRecordMessage : message.outlinerecords())
1637  {
1638  auto pOutlineRecord = std::make_shared<OutlineRecord>();
1639  pOutlineRecord->from_protobuf(outlineRecordMessage);
1640  m_outlineRecords.push_back(pOutlineRecord);
1641  }
1642 
1643  for (const auto& kvAttributeAssignment : message.attributelookuptable())
1644  {
1645  m_attributeLookupTable[kvAttributeAssignment.first] = kvAttributeAssignment.second;
1646  }
1647  }
1648 
1649  // Inherited via IProtoBuffable
1650  std::unique_ptr<Odb::Lib::Protobuf::EdaDataFile::PackageRecord::OutlineRecord> EdaDataFile::PackageRecord::OutlineRecord::to_protobuf() const
1651  {
1652  std::unique_ptr<Odb::Lib::Protobuf::EdaDataFile::PackageRecord::OutlineRecord> pOutlineRecordMessage(new Odb::Lib::Protobuf::EdaDataFile::PackageRecord::OutlineRecord);
1653  pOutlineRecordMessage->set_type((Odb::Lib::Protobuf::EdaDataFile::PackageRecord::OutlineRecord::Type)type);
1654  pOutlineRecordMessage->set_lowerleftx(lowerLeftX);
1655  pOutlineRecordMessage->set_lowerlefty(lowerLeftY);
1656  pOutlineRecordMessage->set_width(width);
1657  pOutlineRecordMessage->set_height(height);
1658  pOutlineRecordMessage->set_xcenter(xCenter);
1659  pOutlineRecordMessage->set_ycenter(yCenter);
1660  pOutlineRecordMessage->set_radius(radius);
1661  pOutlineRecordMessage->set_halfside(halfSide);
1662  for (const auto& contourPolygon : m_contourPolygons)
1663  {
1664  auto pContourPolygonMessage = pOutlineRecordMessage->add_contourpolygons();
1665  pContourPolygonMessage->CopyFrom(*contourPolygon->to_protobuf());
1666  }
1667  return pOutlineRecordMessage;
1668  }
1669 
1670  void EdaDataFile::PackageRecord::OutlineRecord::from_protobuf(const Odb::Lib::Protobuf::EdaDataFile::PackageRecord::OutlineRecord& message)
1671  {
1672  type = (Type)message.type();
1673  lowerLeftX = message.lowerleftx();
1674  lowerLeftY = message.lowerlefty();
1675  width = message.width();
1676  height = message.height();
1677  xCenter = message.xcenter();
1678  yCenter = message.ycenter();
1679  radius = message.radius();
1680  halfSide = message.halfside();
1681  for (const auto& contourPolygonMessage : message.contourpolygons())
1682  {
1683  auto pContourPolygon = std::make_shared<ContourPolygon>();
1684  pContourPolygon->from_protobuf(contourPolygonMessage);
1685  m_contourPolygons.push_back(pContourPolygon);
1686  }
1687  }
1688 
1689  // Inherited via IProtoBuffable
1690  std::unique_ptr<Odb::Lib::Protobuf::EdaDataFile::PackageRecord::PinRecord>
1691  EdaDataFile::PackageRecord::PinRecord::to_protobuf() const
1692  {
1693  std::unique_ptr<Odb::Lib::Protobuf::EdaDataFile::PackageRecord::PinRecord> pPinRecordMessage(new Odb::Lib::Protobuf::EdaDataFile::PackageRecord::PinRecord);
1694  pPinRecordMessage->set_name(name);
1695  pPinRecordMessage->set_type((Odb::Lib::Protobuf::EdaDataFile::PackageRecord::PinRecord::Type)type);
1696  pPinRecordMessage->set_xcenter(xCenter);
1697  pPinRecordMessage->set_ycenter(yCenter);
1698  pPinRecordMessage->set_finishedholesize(finishedHoleSize);
1699  pPinRecordMessage->set_electricaltype((Odb::Lib::Protobuf::EdaDataFile::PackageRecord::PinRecord::ElectricalType)electricalType);
1700  pPinRecordMessage->set_mounttype((Odb::Lib::Protobuf::EdaDataFile::PackageRecord::PinRecord::MountType)mountType);
1701  pPinRecordMessage->set_id(id);
1702  pPinRecordMessage->set_index(index);
1703  return pPinRecordMessage;
1704  }
1705 
1706  void EdaDataFile::PackageRecord::PinRecord::from_protobuf(const Odb::Lib::Protobuf::EdaDataFile::PackageRecord::PinRecord& message)
1707  {
1708  name = message.name();
1709  type = (Type)message.type();
1710  xCenter = message.xcenter();
1711  yCenter = message.ycenter();
1712  finishedHoleSize = message.finishedholesize();
1713  electricalType = (ElectricalType)message.electricaltype();
1714  mountType = (MountType)message.mounttype();
1715  id = message.id();
1716  index = message.index();
1717  }
1718 }