OdbDesignLib
OdbDesign ODB++ Parsing Library
All Classes
FileArchive.cpp
1 #include "FileArchive.h"
2 #include <filesystem>
3 #include "ArchiveExtractor.h"
4 #include "MiscInfoFile.h"
5 #include "Logger.h"
6 #include "StopWatch.h"
7 #include "fastmove.h"
8 #include <system_error>
9 #include <cstdio>
10 #include <string>
11 #include <memory>
12 
13 using namespace Utils;
14 using namespace std::filesystem;
15 
16 namespace Odb::Lib::FileModel::Design
17 {
18  FileArchive::FileArchive()
19  : m_filePath()
20  {
21  }
22 
23  FileArchive::FileArchive(const std::string& path)
24  : m_filePath(path)
25  {
26  }
27 
28  FileArchive::~FileArchive()
29  {
30  m_stepsByName.clear();
31  m_symbolsDirectoriesByName.clear();
32  }
33 
34  std::string FileArchive::GetRootDir() const
35  {
36  return m_rootDir;
37  }
38 
39  std::string FileArchive::GetProductName() const
40  {
41  return m_productName;
42  }
43 
44  std::string FileArchive::GetFilePath() const
45  {
46  return m_filePath;
47  }
48 
49  std::string FileArchive::GetFilename() const
50  {
51  //return m_filename;
52  return path(m_filePath).filename().string();
53  }
54 
55  const StepDirectory::StringMap& FileArchive::GetStepsByName() const
56  {
57  return m_stepsByName;
58  }
59 
60  const SymbolsDirectory::StringMap& FileArchive::GetSymbolsDirectoriesByName() const
61  {
62  return m_symbolsDirectoriesByName;
63  }
64 
65  bool FileArchive::ParseFileModel()
66  {
67  //try
68  {
69  StopWatch timer(true);
70 
71  if (!exists(m_filePath)) return false;
72 
73  if (is_regular_file(m_filePath))
74  {
75  path extractedPath;
76  if (!ExtractDesignArchive(m_filePath, extractedPath))
77  {
78  logerror("failed to extract archive: (" + m_filePath + ")");
79  return false;
80  }
81 
82  m_rootDir = findRootDir(extractedPath);
83  }
84 
85  if (is_directory(m_rootDir))
86  {
87  loginfo("Parsing... ");
88 
89  if (ParseDesignDirectory(m_rootDir))
90  {
91  timer.stop();
92  auto s = timer.getElapsedSecondsString();
93  loginfo("Successfully parsed. (" + s + "s)");
94 
95  return true;
96  }
97  else
98  {
99  logerror("Parsing failed.");
100  throw std::runtime_error("Parsing failed.");
101  }
102  }
103  else
104  {
105  logerror("Failed to find root directory");
106  }
107  }
108  //catch (std::exception& e)
109  //{
110  // logexception(e);
111  // throw e;
112  //}
113 
114  return false;
115  }
116 
117  bool FileArchive::SaveFileModel(const path& directory)
118  {
119  return SaveFileModel(directory, m_productName);
120  }
121 
122  bool FileArchive::SaveFileModel(const path& directory, const std::string& archiveName)
123  {
124  // create directory in /tmp
125  // write dir structure and files to it
126  // gzip with archiveName
127  // move archive to directory
128 
129  char szTmpNameBuff[L_tmpnam] = { 0 };
130  if (nullptr == std::tmpnam(szTmpNameBuff)) return false;
131 
132  auto tempPath = temp_directory_path() / szTmpNameBuff;
133  if (!create_directory(tempPath)) return false;
134 
135  auto rootPath = tempPath / archiveName;
136  if (!create_directory(rootPath)) return false;
137  if (!Save(rootPath)) return false;
138 
139  // compress the written file structure
140  std::string createdArchivePath;
141  if (! Utils::ArchiveExtractor::CompressDir(rootPath.string(), tempPath.string(), archiveName, createdArchivePath)) return false;
142  if (createdArchivePath.empty()) return false;
143 
144  // move the compressed file to the requested save directory
145  path archiveFilename = path(createdArchivePath).filename();
146  path destPath = directory / archiveFilename;
147  std::error_code ec;
148  Utils::fastmove_file(createdArchivePath, destPath, true, ec);
149  if (ec.value() != 0) return false;
150 
151  return true;
152  }
153 
154  bool FileArchive::Save(const path& directory)
155  {
156  // MiscInfoFile
157  auto miscPath = directory / "misc";
158  if (!create_directory(miscPath)) return false;
159 
160  std::ofstream ofs1(miscPath / "info", std::ios::out);
161  if (!m_miscInfoFile.Save(ofs1)) return false;
162  ofs1.close();
163 
164  // MiscAttrFile
165  std::ofstream ofs2(miscPath / "sysattr");
166  if (!m_miscAttrListFile.Save(ofs2)) return false;
167  ofs2.close();
168 
169  // StandardFontsFile
170  auto fontsPath = directory / "fonts";
171  if (!create_directory(fontsPath)) return false;
172  std::ofstream ofs3(fontsPath / "standard");
173  if (!m_standardFontsFile.Save(ofs3)) return false;
174  ofs3.close();
175 
176  // MatrixFile
177  auto matrixPath = directory / "matrix";
178  if (!create_directory(matrixPath)) return false;
179  std::ofstream ofs4(matrixPath / "matrix");
180  if (!m_matrixFile.Save(ofs4)) return false;
181  ofs4.close();
182 
183  // Steps
184  const auto stepsDirectory = directory / "steps";
185  if (!create_directory(stepsDirectory)) return false;
186  for (auto& kvStepDirectory : m_stepsByName)
187  {
188  if (!kvStepDirectory.second->Save(stepsDirectory)) return false;
189  }
190 
191  // Symbols
192  const auto symbolsDirectory = directory / "symbols";
193  if (!create_directory(symbolsDirectory)) return false;
194  for (auto& kvSymbolsDirectory : m_symbolsDirectoriesByName)
195  {
196  if (!kvSymbolsDirectory.second->Save(symbolsDirectory)) return false;
197  }
198 
199  return true;
200  }
201 
202  bool FileArchive::ExtractDesignArchive(const path& archivePath, path& extractedPath)
203  {
204  loginfo("Extracting... ");
205 
206  if (!Utils::ArchiveExtractor::IsArchiveTypeSupported(archivePath))
207  {
208  logerror("Unsupported archive type: (" + archivePath.string() + ")");
209  return false;
210  }
211 
212  Utils::ArchiveExtractor extractor(archivePath.string());
213  if (!extractor.Extract()) return false;
214 
215  auto extracted = path(extractor.GetExtractionDirectory());
216  if (!exists(extracted)) return false;
217 
218  extractedPath = extracted;
219 
220  loginfo("Successfully extracted.");
221 
222  return true;
223 
224  }
225 
226  /*static*/ std::string FileArchive::findRootDir(const path& extractedPath)
227  {
228  if (pathContainsTopLevelDesignDirs(extractedPath))
229  {
230  return extractedPath.string();
231  }
232  else
233  {
234  for (const auto& p : directory_iterator(extractedPath))
235  {
236  if (is_directory(p))
237  {
238  if (pathContainsTopLevelDesignDirs(p.path()))
239  {
240  return p.path().string();
241  }
242  }
243  }
244 
245  return "";
246  }
247  }
248 
249  /*static*/ bool FileArchive::pathContainsTopLevelDesignDirs(const path& path)
250  {
251  for (const auto& topLevelRootDirName : TOPLEVEL_DESIGN_DIR_NAMES)
252  {
253  auto rootLevelDirPath = path / topLevelRootDirName;
254  if (!exists(rootLevelDirPath)) return false;
255  }
256  return true;
257  }
258 
259  std::unique_ptr<Protobuf::FileArchive> FileArchive::to_protobuf() const
260  {
261  std::unique_ptr<Protobuf::FileArchive> pFileArchiveMessage(new Protobuf::FileArchive);
262  pFileArchiveMessage->set_productname(m_productName);
263  pFileArchiveMessage->set_filename(m_filename);
264  //pFileArchiveMessage->set_filepath(m_filePath);
265  pFileArchiveMessage->mutable_matrixfile()->CopyFrom(*m_matrixFile.to_protobuf());
266  pFileArchiveMessage->mutable_miscinfofile()->CopyFrom(*m_miscInfoFile.to_protobuf());
267  pFileArchiveMessage->mutable_standardfontsfile()->CopyFrom(*m_standardFontsFile.to_protobuf());
268  pFileArchiveMessage->mutable_miscattrlistfile()->CopyFrom(*m_miscAttrListFile.to_protobuf());
269 
270  for (const auto& kvStepDirectoryRecord : m_stepsByName)
271  {
272  (*pFileArchiveMessage->mutable_stepsbyname())[kvStepDirectoryRecord.first] = *kvStepDirectoryRecord.second->to_protobuf();
273  }
274 
275  for (const auto& kvSymbolsDirectory : m_symbolsDirectoriesByName)
276  {
277  (*pFileArchiveMessage->mutable_symbolsdirectoriesbyname())[kvSymbolsDirectory.first] = *kvSymbolsDirectory.second->to_protobuf();
278  }
279 
280  return pFileArchiveMessage;
281  }
282 
283  void FileArchive::from_protobuf(const Protobuf::FileArchive& message)
284  {
285  m_productName = message.productname();
286  m_filename = message.filename();
287  //m_filePath = message.filepath();
288  m_matrixFile.from_protobuf(message.matrixfile());
289  m_miscInfoFile.from_protobuf(message.miscinfofile());
290  m_standardFontsFile.from_protobuf(message.standardfontsfile());
291  m_miscAttrListFile.from_protobuf(message.miscattrlistfile());
292 
293  for (const auto& kvStepDirectoryRecord : message.stepsbyname())
294  {
295  auto pStepDirectory = std::make_shared<StepDirectory>("");
296  pStepDirectory->from_protobuf(kvStepDirectoryRecord.second);
297  m_stepsByName[kvStepDirectoryRecord.first] = pStepDirectory;
298  }
299 
300  for (const auto& kvSymbolsDirectory : message.symbolsdirectoriesbyname())
301  {
302  auto pSymbolsDirectory = std::make_shared<SymbolsDirectory>("");
303  pSymbolsDirectory->from_protobuf(kvSymbolsDirectory.second);
304  m_symbolsDirectoriesByName[kvSymbolsDirectory.first] = pSymbolsDirectory;
305  }
306  }
307 
308  bool FileArchive::ParseDesignDirectory(const path& path)
309  {
310  if (!exists(path)) return false;
311  else if (!is_directory(path)) return false;
312 
313  m_productName = path.stem().string();
314 
315  if (! ParseStepDirectories(path)) return false;
316  if (! ParseMiscInfoFile(path)) return false;
317  if (! ParseMatrixFile(path)) return false;
318  if (! ParseStandardFontsFile(path)) return false;
319  if (! ParseSymbolsDirectories(path)) return false;
320  if (! ParseMiscAttrListFile(path)) return false;
321 
322  return true;
323  }
324 
325  bool FileArchive::ParseStepDirectories(const path& path)
326  {
327  loginfo("Parsing steps...");
328 
329  auto stepsPath = path / "steps";
330  for (auto& d : directory_iterator(stepsPath))
331  {
332  if (is_directory(d))
333  {
334  auto pStep = std::make_shared<StepDirectory>(d.path());
335  if (pStep->Parse())
336  {
337  m_stepsByName[pStep->GetName()] = pStep;
338  }
339  else
340  {
341  logwarn("Failed to parse step: " + pStep->GetName());
342  //return false;
343  }
344  }
345  }
346 
347  loginfo("Parsing steps complete");
348 
349  return true;
350  }
351 
352  bool FileArchive::ParseMiscInfoFile(const path& path)
353  {
354  loginfo("Parsing misc/info file...");
355 
356  auto miscDirectory = path / "misc";
357  if (!exists(miscDirectory)) return false;
358  if (!is_directory(miscDirectory)) return false;
359 
360  if (!m_miscInfoFile.Parse(miscDirectory)) return false;
361 
362  loginfo("Parsing misc/info file complete");
363 
364  return true;
365  }
366 
367  bool FileArchive::ParseMiscAttrListFile(const path& path)
368  {
369  loginfo("Parsing misc/attrlist file...");
370 
371  auto miscDirectory = path / "misc";
372  if (!exists(miscDirectory)) return false;
373  if (!is_directory(miscDirectory)) return false;
374 
375  if (!m_miscAttrListFile.Parse(miscDirectory)) return false;
376 
377  loginfo("Parsing misc/attrlist file complete");
378 
379  return true;
380  }
381 
382  bool FileArchive::ParseMatrixFile(const path& path)
383  {
384  loginfo("Parsing matrix/matrix file...");
385 
386  auto matrixDir = path / "matrix";
387  if (!exists(matrixDir)) return false;
388  if (!is_directory(matrixDir)) return false;
389 
390  if (!m_matrixFile.Parse(matrixDir)) return false;
391 
392  loginfo("Parsing matrix/matrix file complete");
393 
394  return true;
395  }
396  bool FileArchive::ParseStandardFontsFile(const path& path)
397  {
398  loginfo("Parsing fonts/standard file...");
399 
400  auto fontsDir = path / "fonts";
401  if (!exists(fontsDir)) return false;
402  if (!is_directory(fontsDir)) return false;
403 
404  if (!m_standardFontsFile.Parse(fontsDir)) return false;
405 
406  loginfo("Parsing fonts/standard file complete");
407 
408  return true;
409  }
410 
411  bool FileArchive::ParseSymbolsDirectories(const path& path)
412  {
413  loginfo("Parsing symbols root directory...");
414 
415  auto symbolsDirectory = path / "symbols";
416 
417  if (!exists(symbolsDirectory))
418  {
419  logwarn("symbols root directory does not exist (" + symbolsDirectory.string() + ")");
420  return true;
421  }
422  else if (!is_directory(symbolsDirectory))
423  {
424  logerror("symbols root directory exists but is a regular file/not a directory (" + symbolsDirectory.string() + ")");
425  return false;
426  }
427 
428  for (auto& d : directory_iterator(symbolsDirectory))
429  {
430  if (is_directory(d))
431  {
432  auto pSymbolsDirectory = std::make_shared<SymbolsDirectory>(d.path());
433  if (pSymbolsDirectory->Parse())
434  {
435  //loginfo("Parsing symbols directory: " + pSymbolsDirectory->GetName() + " complete");
436 
437  m_symbolsDirectoriesByName[pSymbolsDirectory->GetName()] = pSymbolsDirectory;
438  }
439  else
440  {
441  logerror("Parsing symbol directory: " + pSymbolsDirectory->GetName() + " failed");
442  return false;
443  }
444  }
445  }
446 
447  loginfo("Parsing symbols root directory complete");
448 
449  return true;
450  }
451 
452  const MiscInfoFile &FileArchive::GetMiscInfoFile() const
453  {
454  return m_miscInfoFile;
455  }
456 
457  const MatrixFile& FileArchive::GetMatrixFile() const
458  {
459  return m_matrixFile;
460  }
461 
462  const StandardFontsFile& FileArchive::GetStandardFontsFile() const
463  {
464  return m_standardFontsFile;
465  }
466 
467  const AttrListFile& FileArchive::GetMiscAttrListFile() const
468  {
469  return m_miscAttrListFile;
470  }
471 
472  std::shared_ptr<StepDirectory> FileArchive::GetStepDirectory(const std::string& stepName /*= ""*/) const
473  {
474  std::shared_ptr<FileModel::Design::StepDirectory> pStepDirectory;
475 
476  const auto& steps = GetStepsByName();
477  if (!steps.empty())
478  {
479  if (stepName.empty())
480  {
481  // return first step
482  pStepDirectory = steps.begin()->second;
483  }
484  else
485  {
486  auto findIt = steps.find(stepName);
487  if (findIt != steps.end())
488  {
489  pStepDirectory = findIt->second;
490  }
491  }
492  }
493 
494  return pStepDirectory;
495  }
496 }