[doc_tool] Moved executing of external programms from Model to FSHelper
[hsd_doku_tool.git] / src / Model.cxx
1 ///////////////////////////////////////////////////////////////////////////
2 // Workfile: Model.cxx
3 // Author: Daniel Giritzer <daniel@giritzer.eu>
4 // Date: 25.03.2018
5 // Description: Diese Klasse stellt die Daten und Programmlogik dieses
6 //              Programmes dar.
7 // Remarks:
8 ///////////////////////////////////////////////////////////////////////////
9 #include "Model.h"
10 #include <iostream>
11 #include <algorithm>
12 #include <fstream>
13 #include <sstream>
14
15 void Model::throwXmlError(tinyxml2::XMLError error)
16 {
17     if (error != tinyxml2::XML_SUCCESS)
18     {
19         if(error == tinyxml2::XML_ERROR_FILE_NOT_FOUND)
20             throw std::string("XML config file not found!");
21
22         if(error == tinyxml2::XML_ERROR_FILE_COULD_NOT_BE_OPENED)
23             throw std::string("XML config could not be opened!");
24
25         if(error == tinyxml2::XML_ERROR_EMPTY_DOCUMENT)
26             throw std::string("XML config is emty!");
27
28         if(error == tinyxml2::XML_ERROR_FILE_READ_ERROR)
29             throw std::string("XML config could not be read!");
30
31         if(error == tinyxml2::XML_WRONG_ATTRIBUTE_TYPE)
32             throw std::string("Error, wrong attribute type!");
33
34         if(error == tinyxml2::XML_NO_ATTRIBUTE)
35             throw std::string("Error, no attribute!");
36
37         if(error == tinyxml2::XML_ERROR_PARSING_ELEMENT)
38             throw std::string("Error parsing element!");
39
40         if(error == tinyxml2::XML_ERROR_PARSING_ATTRIBUTE)
41             throw std::string("Error parsing attribute!");
42
43         if(error == tinyxml2::XML_ERROR_PARSING_TEXT)
44             throw std::string("Error parsing text!");
45
46         if(error == tinyxml2::XML_ERROR_PARSING_CDATA)
47             throw std::string("Error parsing CDATA!");
48
49         if(error == tinyxml2::XML_ERROR_PARSING_COMMENT)
50             throw std::string("Error parsing comment!");
51
52         if(error == tinyxml2::XML_ERROR_PARSING_DECLARATION)
53             throw std::string("Error parsing declaration!");
54
55         if(error == tinyxml2::XML_ERROR_PARSING_UNKNOWN)
56             throw std::string("Error parsing unknown!");
57
58         if(error == tinyxml2::XML_ERROR_MISMATCHED_ELEMENT)
59             throw std::string("Error mismatched element!");
60
61         if(error == tinyxml2::XML_ERROR_PARSING)
62             throw std::string("Parsing Error!");
63
64         if(error == tinyxml2::XML_CAN_NOT_CONVERT_TEXT)
65             throw std::string("Error, could not convert text!");
66
67         if(error == tinyxml2::XML_NO_TEXT_NODE)
68             throw std::string("Error, no text node!");
69
70         throw std::string("Unknown XML Error!");
71     }
72 }
73
74 Model::Model(std::string confDir, FSHelper help, std::string log)
75 {
76     mFSHelp = help;
77
78     if(!mFSHelp.dirExists(confDir))
79         throw std::string("Configuration directory not found!");
80
81     mLogFile = log;
82     mConfDir = mFSHelp.addDirLastSlash(confDir);
83 }
84
85 void Model::clearMembers()
86 {
87     mAuthors.clear();
88     mChapters.clear();
89     mTemplFileString.clear();
90     mOutPath.clear();
91     mSourcePath.clear();
92     mCoverSheet.clear();
93     //mConfDir.clear();
94     mDocName.clear();
95     mSubject.clear();
96     bool mDoxygen=false;
97     mDoxygenName.clear();
98     bool mSourceChapter=false;
99     mSourceChapterName.clear();
100     bool mHasCoverSheet=false;
101     int mChapterNum=1;
102     int mAuthorNum=0;
103 }
104
105 void Model::newTemplate(std::string name)
106 {
107     setSubjectName(name);
108     chapterInsertBack("Example", "Example.tex");
109     authorInsertBack();
110     mChapterNum = 1;
111     mAuthorNum = 0;
112     getSelChapter()->SetText("%Example Chapter");
113 }
114
115 void Model::setTemplFile(std::string templFile)
116 {
117
118     clearMembers();
119
120     if(templFile == "")
121         throw std::string("Invalid config file!");
122
123     // Lade XML Datei
124     throwXmlError(mTemplFile.LoadFile(templFile.c_str()));
125     // Speichere XML Dateinamen + Pfad
126     mTemplFileString = templFile;
127
128     if(mTemplFile.FirstChildElement("docsettings")==nullptr)
129         throw std::string("XML Syntax error: Missing <docsettings> tag");
130
131     //------------ Parse Fileextensions ------------
132     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("fileextensions") == nullptr)
133         throw std::string("Missing config: <fileextensions>");
134
135     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("fileextensions")->FirstChildElement("source") == nullptr)
136         throw std::string("Missing config: <fileextensions> -> <source>");
137
138     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("fileextensions")->FirstChildElement("header") == nullptr)
139         throw std::string("Missing config: <fileextensions> -> <header>");
140
141     mSourceExtension = mTemplFile.FirstChildElement("docsettings")->FirstChildElement("fileextensions")->FirstChildElement("source")->GetText();
142     mHeaderExtension = mTemplFile.FirstChildElement("docsettings")->FirstChildElement("fileextensions")->FirstChildElement("header")->GetText();
143
144     //------------ Parse Location ------------
145     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("location") == nullptr)
146         throw std::string("XML parsing error while loading location!");
147
148     mLocation = mTemplFile.FirstChildElement("docsettings")->FirstChildElement("location")->GetText();
149
150     //------------ Parse Template Typ ------------
151     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("subjectname") == nullptr)
152         throw std::string("XML parsing error while loading subject name!");
153
154     mSubject = mTemplFile.FirstChildElement("docsettings")->FirstChildElement("subjectname")->GetText();
155
156     //------------ Parse Abteilung Koordinaten ------------
157     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("department") == nullptr)
158         throw std::string("Missing config: <docsettings> -> <department>");
159
160     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("department")->FirstChildElement("x_coor") == nullptr)
161         throw std::string("Missing config: <docsettings> -> <department> -> <x_coor>");
162
163     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("department")->FirstChildElement("y_coor") == nullptr)
164         throw std::string("Missing config: <docsettings> -> <department> -> <y_coor>");
165
166     throwXmlError(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("department")->FirstChildElement("x_coor")->QueryUnsignedText(&mDepartmentX));
167     throwXmlError(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("department")->FirstChildElement("y_coor")->QueryUnsignedText(&mDepartmentY));
168
169     //------------ Parse Titelseite ------------
170     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("cover_sheet") == nullptr)
171         throw std::string("Missing config: <docsettings> -> <cover_sheet>");
172
173     std::string str = mTemplFile.FirstChildElement("docsettings")->FirstChildElement("cover_sheet")->GetText();
174     if(str == "true")
175         mHasCoverSheet = true;
176     else if(str == "false")
177         mHasCoverSheet = false;
178     else
179         throw std::string("Wrong config value <docsettings> -> <cover_sheet>: " + str);
180
181
182
183     //------------ Parse Quellcode Kapitel ------------
184     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("sourcecode")==nullptr)
185         throw std::string("Missing config: <docsettings> -> <sourcecode>");
186
187     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("sourcecode")->FirstChildElement("enabled")==nullptr)
188         throw std::string("Missing config: <docsettings> -> <sourcecode> -> <enabled>");
189
190     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("sourcecode")->FirstChildElement("chaptername")==nullptr)
191         throw std::string("Missing config: <docsettings> -> <sourcecode> -> <chaptername>");
192
193     mSourceChapterName = mTemplFile.FirstChildElement("docsettings")->FirstChildElement("sourcecode")->FirstChildElement("chaptername")->GetText();
194     str = mTemplFile.FirstChildElement("docsettings")->FirstChildElement("sourcecode")->FirstChildElement("enabled")->GetText();
195     if(str == "true")
196         mSourceChapter = true;
197     else if(str == "false")
198         mSourceChapter = false;
199     else
200         throw std::string("Wrong config value <docsettings> -> <sourcecode> -> <enabled>: " + str);
201
202
203
204     //------------ Parse Doxygen Kapitel ------------
205     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("doxygen")==nullptr)
206         throw std::string("Missing config: <docsettings> -> <doxygen>");
207
208     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("doxygen")->FirstChildElement("enabled")==nullptr)
209         throw std::string("Missing config: <docsettings> -> <doxygen> -> <enabled>");
210
211     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("doxygen")->FirstChildElement("chaptername")==nullptr)
212         throw std::string("Missing config: <docsettings> -> <doxygen> -> <chaptername>");
213
214     mDoxygenName = mTemplFile.FirstChildElement("docsettings")->FirstChildElement("doxygen")->FirstChildElement("chaptername")->GetText();
215     str = mTemplFile.FirstChildElement("docsettings")->FirstChildElement("doxygen")->FirstChildElement("enabled")->GetText();
216     if(str == "true")
217         mDoxygen = true;
218     else if(str == "false")
219         mDoxygen = false;
220     else
221         throw std::string("Wrong config value <docsettings> -> <doxygen> -> <enabled>: " + str);
222
223
224     //------------ Parse Kapitel ------------
225     Chapter curChap;
226     int val = 0;
227
228     if(mTemplFile.FirstChildElement("chapters")==nullptr)
229         throw std::string("XML Syntax error: Missing <chapters> tag");
230
231     tinyxml2::XMLElement* curElem = mTemplFile.FirstChildElement("chapters")->FirstChildElement("chapter");
232
233     // Erstes Element
234     if( curElem == nullptr ||
235             curElem->FirstChildElement("num") == nullptr ||
236             curElem->FirstChildElement("filename") == nullptr ||
237             curElem->FirstChildElement("prettyName") == nullptr)
238         throw std::string("XML Syntax error parsing Chapter content.");
239
240     curElem->FirstChildElement("num")->QueryIntText(&val);
241     curChap.SetNum(val);
242     curChap.SetOutFileName(curElem->FirstChildElement("filename")->GetText());
243     curChap.SetPrettyName(curElem->FirstChildElement("prettyName")->GetText());
244     curChap.SetDone(false);
245     curChap.SetText(curElem->FirstChildElement("textTemplate")->GetText());
246     mChapters.push_back(std::make_shared<Chapter>(curChap));
247
248     // Alle weiteren Elemente
249     while(curElem != mTemplFile.FirstChildElement("chapters")->LastChildElement("chapter"))
250     {
251         curElem = curElem->NextSiblingElement("chapter");
252
253         if( curElem == nullptr ||
254                 curElem->FirstChildElement("num") == nullptr ||
255                 curElem->FirstChildElement("filename") == nullptr ||
256                 curElem->FirstChildElement("prettyName") == nullptr)
257             throw std::string("XML Syntax error parsing Chapter content.");
258
259         curElem->FirstChildElement("num")->QueryIntText(&val);
260         curChap.SetNum(val);
261         curChap.SetOutFileName(curElem->FirstChildElement("filename")->GetText());
262         curChap.SetPrettyName(curElem->FirstChildElement("prettyName")->GetText());
263         curChap.SetDone(false);
264         curChap.SetText(curElem->FirstChildElement("textTemplate")->GetText());
265         mChapters.push_back(std::make_shared<Chapter>(curChap));
266     }
267     mChapterNum = 1;
268
269     //------------ Parse Author ------------
270     Author curAuthor;
271     unsigned int num = 0;
272     unsigned int name_x = 0;
273     unsigned int name_y = 0;
274     unsigned int time_spent_x = 0;
275     unsigned int time_spent_y = 0;
276     unsigned int time_est_x = 0;
277     unsigned int time_est_y = 0;
278
279     if(mTemplFile.FirstChildElement("docsettings")->FirstChildElement("authors")==nullptr)
280         throw std::string("XML Syntax error: Missing <authors> tag");
281
282     curElem = mTemplFile.FirstChildElement("docsettings")->FirstChildElement("authors")->FirstChildElement("author");
283
284     // Erstes Element
285     if( curElem == nullptr ||
286             curElem->FirstChildElement("num") == nullptr ||
287             curElem->FirstChildElement("x_coor") == nullptr ||
288             curElem->FirstChildElement("y_coor") == nullptr ||
289             curElem->FirstChildElement("time_spent")->FirstChildElement("x_coor") == nullptr ||
290             curElem->FirstChildElement("time_spent")->FirstChildElement("y_coor") == nullptr ||
291             curElem->FirstChildElement("time_estimated")->FirstChildElement("x_coor") == nullptr ||
292             curElem->FirstChildElement("time_estimated")->FirstChildElement("y_coor") == nullptr)
293         throw std::string("XML syntax error parsing author content.");
294
295     throwXmlError(curElem->FirstChildElement("num")->QueryUnsignedText(&num));
296     throwXmlError(curElem->FirstChildElement("x_coor")->QueryUnsignedText(&name_x));
297     throwXmlError(curElem->FirstChildElement("y_coor")->QueryUnsignedText(&name_y));
298     throwXmlError(curElem->FirstChildElement("time_spent")->FirstChildElement("x_coor")->QueryUnsignedText(&time_spent_x));
299     throwXmlError(curElem->FirstChildElement("time_spent")->FirstChildElement("y_coor")->QueryUnsignedText(&time_spent_y));
300     throwXmlError(curElem->FirstChildElement("time_estimated")->FirstChildElement("x_coor")->QueryUnsignedText(&time_est_x));
301     throwXmlError(curElem->FirstChildElement("time_estimated")->FirstChildElement("y_coor")->QueryUnsignedText(&time_est_y));
302     curAuthor.SetNum(num);
303     curAuthor.SetXNameCoordinate(name_x);
304     curAuthor.SetYNameCoordinate(name_y);
305     curAuthor.SetXTimeEstimatedCoordinate(time_est_x);
306     curAuthor.SetYTimeEstimatedCoordinate(time_est_y);
307     curAuthor.SetXTimeSpentCoordinate(time_spent_x);
308     curAuthor.SetYTimeSpentCoordinate(time_spent_y);
309     curAuthor.SetName("Author " + std::to_string(num));
310     curAuthor.SetTimeEstimated("0 h");
311     curAuthor.SetTimeSpent("0 h");
312     mAuthors.push_back(std::make_shared<Author>(curAuthor));
313
314     // Alle weiteren Elemente
315     while(curElem != mTemplFile.FirstChildElement("docsettings")->FirstChildElement("authors")->LastChildElement("author"))
316     {
317         curElem = curElem->NextSiblingElement("author");
318
319         if( curElem == nullptr ||
320                 curElem->FirstChildElement("num") == nullptr ||
321                 curElem->FirstChildElement("x_coor") == nullptr ||
322                 curElem->FirstChildElement("y_coor") == nullptr ||
323                 curElem->FirstChildElement("time_spent")->FirstChildElement("x_coor") == nullptr ||
324                 curElem->FirstChildElement("time_spent")->FirstChildElement("y_coor") == nullptr ||
325                 curElem->FirstChildElement("time_estimated")->FirstChildElement("x_coor") == nullptr ||
326                 curElem->FirstChildElement("time_estimated")->FirstChildElement("y_coor") == nullptr)
327             throw std::string("XML syntax error parsing author content.");
328
329         throwXmlError(curElem->FirstChildElement("num")->QueryUnsignedText(&num));
330         throwXmlError(curElem->FirstChildElement("x_coor")->QueryUnsignedText(&name_x));
331         throwXmlError(curElem->FirstChildElement("y_coor")->QueryUnsignedText(&name_y));
332         throwXmlError(curElem->FirstChildElement("time_spent")->FirstChildElement("x_coor")->QueryUnsignedText(&time_spent_x));
333         throwXmlError(curElem->FirstChildElement("time_spent")->FirstChildElement("y_coor")->QueryUnsignedText(&time_spent_y));
334         throwXmlError(curElem->FirstChildElement("time_estimated")->FirstChildElement("x_coor")->QueryUnsignedText(&time_est_x));
335         throwXmlError(curElem->FirstChildElement("time_estimated")->FirstChildElement("y_coor")->QueryUnsignedText(&time_est_y));
336         curAuthor.SetNum(num);
337         curAuthor.SetXNameCoordinate(name_x);
338         curAuthor.SetYNameCoordinate(name_y);
339         curAuthor.SetXTimeEstimatedCoordinate(time_est_x);
340         curAuthor.SetYTimeEstimatedCoordinate(time_est_y);
341         curAuthor.SetXTimeSpentCoordinate(time_spent_x);
342         curAuthor.SetYTimeSpentCoordinate(time_spent_y);
343         curAuthor.SetName("Author " + std::to_string(num));
344         curAuthor.SetTimeEstimated("0 h");
345         curAuthor.SetTimeSpent("0 h");
346         mAuthors.push_back(std::make_shared<Author>(curAuthor));
347     }
348     mAuthorNum = 0;
349
350     // Sortiere Listen
351     std::sort(mAuthors.begin(), mAuthors.end(), [](const Author::SPtr first, const Author::SPtr sec)
352     {
353         return first->GetNum() < sec->GetNum();
354     });
355     std::sort(mChapters.begin(), mChapters.end(), [](const Chapter::SPtr first, const Chapter::SPtr sec)
356     {
357         return first->GetNum() < sec->GetNum();
358     });
359 }
360
361 std::list<std::string> Model::getChapterNames()
362 {
363     std::list<std::string> retVal;
364
365     for(auto curChap : mChapters)
366     {
367         retVal.push_back(curChap->GetPrettyName());
368     }
369
370     return retVal;
371 }
372
373 std::list<std::string> Model::getAuthorNames()
374 {
375     std::list<std::string> retVal;
376
377     for(auto curAuth : mAuthors)
378     {
379         retVal.push_back(curAuth->GetName());
380     }
381
382     return retVal;
383 }
384
385 void Model::loadChapter(int num)
386 {
387     mChapterNum = num;
388 }
389
390 void Model::loadAuthor(int num)
391 {
392     mAuthorNum = num;
393 }
394
395 Author::SPtr Model::getSelAuthor()
396 {
397     return mAuthors.at(mAuthorNum);
398 }
399
400 Chapter::SPtr Model::getSelChapter()
401 {
402     return mChapters.at(mChapterNum-1);
403 }
404
405 int Model::getNumChapters()
406 {
407     return mChapters.size();
408 }
409
410 int Model::getNumFinishedChapters()
411 {
412     int retVal=0;
413     for(auto curChapt : mChapters)
414         if(curChapt->GetDone())
415             retVal++;
416
417     return retVal;
418 }
419
420 int Model::getChapterNum()
421 {
422     return mChapterNum;
423 }
424
425 int Model::getAuthorNum()
426 {
427     return mAuthorNum;
428 }
429
430 std::string Model::getSubjectName()
431 {
432     return mSubject;
433 }
434
435 void Model::setSubjectName(std::string name)
436 {
437     if(!mFSHelp.validFileName(name))
438         throw std::string("Subject name contains illegal characters.");
439
440     mSubject = name;
441 }
442
443 void Model::chapterUp()
444 {
445     // Umrechnung, da mChapterNum 1 Basierend ist
446     int chaptElem = mChapterNum-1;
447     if(chaptElem>0)
448     {
449         mChapters.at(chaptElem)->SetNum(mChapterNum-1);
450         mChapters.at(chaptElem-1)->SetNum(mChapterNum);
451         std::iter_swap(mChapters.begin() + chaptElem, mChapters.begin() + chaptElem-1);
452         mChapterNum=mChapterNum-1;
453     }
454 }
455
456 void Model::chapterDown()
457 {
458     // Umrechnung, da mChapterNum 1 Basierend ist
459     int chaptElem = mChapterNum-1;
460     if(chaptElem<getNumChapters()-1)
461     {
462         mChapters.at(chaptElem)->SetNum(mChapterNum+1);
463         mChapters.at(chaptElem+1)->SetNum(mChapterNum);
464         std::iter_swap(mChapters.begin() + chaptElem, mChapters.begin() + chaptElem+1);
465         mChapterNum=mChapterNum+1;
466     }
467 }
468
469 void Model::chapterInsertBack(std::string chaptName, std::string outFileName)
470 {
471     if(chaptName.empty())
472         throw std::string("Please set an chapter name!");
473
474     if(outFileName.empty())
475         throw std::string("Please set an output file name!");
476
477     // Überprüfe ob ein anderes kapitel den selben dateinamen besitzt
478     auto found = std::find_if(mChapters.begin(), mChapters.end(), [outFileName](Chapter::SPtr chapt)
479     {
480         return (outFileName == chapt->GetOutFileName());
481     }
482                              );
483
484     if(found !=  std::end(mChapters))
485         throw std::string("Filename " + outFileName + " is already used by chapter " + std::to_string((*found)->GetNum()) + " (" + (*found)->GetPrettyName()  + ")" );
486
487     Chapter::SPtr newChapt {std::make_shared<Chapter>()};
488     newChapt->SetNum(getNumChapters() + 1);
489     newChapt->SetOutFileName(outFileName);
490     newChapt->SetPrettyName(chaptName);
491     newChapt->SetDone(false);
492     mChapters.push_back(newChapt);
493     setOutPath(mOutPath); // Lade text aus hinzugefügter kapiteldatei, falls sie existiert
494 }
495
496 void Model::authorInsertBack()
497 {
498     Author::SPtr newAuthor {std::make_shared<Author>()};
499     newAuthor->SetNum(mAuthors.size()+1);
500     newAuthor->SetXNameCoordinate(0);
501     newAuthor->SetYNameCoordinate(0);
502     newAuthor->SetXTimeEstimatedCoordinate(0);
503     newAuthor->SetYTimeEstimatedCoordinate(0);
504     newAuthor->SetXTimeSpentCoordinate(0);
505     newAuthor->SetYTimeSpentCoordinate(0);
506     newAuthor->SetTimeEstimated("0 h");
507     newAuthor->SetTimeSpent("0 h");
508
509     newAuthor->SetName("Author " + std::to_string(mAuthors.size()+1));
510     mAuthors.push_back(newAuthor);
511 }
512
513 void Model::chapterRemove()
514 {
515     if(getNumChapters()-1>0)
516     {
517         std::vector <Chapter::SPtr>::iterator it;
518         for( it = mChapters.begin()+mChapterNum-1; it != mChapters.end(); it++)
519             (*it)->SetNum((*it)->GetNum()-1);
520
521         mChapters.erase(mChapters.begin() + mChapterNum-1);
522         mChapterNum=1;
523     }
524 }
525
526 void Model::authorRemove()
527 {
528     if(mAuthors.size()-1>0)
529     {
530         std::vector <Author::SPtr>::iterator it;
531         for( it = mAuthors.begin()+mAuthorNum; it != mAuthors.end(); it++)
532         {
533             if((*it)->GetName() == "Author " + std::to_string((*it)->GetNum()))
534                 (*it)->SetName("Author " + std::to_string((*it)->GetNum()-1));
535
536             (*it)->SetNum((*it)->GetNum()-1);
537         }
538
539         mAuthors.erase(mAuthors.begin() + mAuthorNum);
540         mAuthorNum=0;
541     }
542 }
543
544 void Model::setOutPath(std::string outPath)
545 {
546     if(!mFSHelp.dirExists(outPath))
547         throw std::string("Output path not found!");
548
549     mOutPath = mFSHelp.addDirLastSlash(outPath);
550
551     // Lade text aus ev. existierenden Dateien
552     for(auto curChapt : mChapters)
553     {
554         std::ifstream iFstr;
555         iFstr.open(mOutPath + curChapt->GetOutFileName());
556         if(iFstr.good())
557         {
558             std::stringstream buffer;
559             buffer << iFstr.rdbuf();
560             if(buffer.good())
561                 curChapt->SetText(buffer.str());
562             else
563                 throw std::string("Error reading existing chapter file!");
564
565         }
566         iFstr.close();
567     }
568 }
569
570 void Model::generateDocument()
571 {
572     // Überprüfe dateien + pfade und versuche diese zu kopieren
573     if(!mFSHelp.dirExists(mOutPath))
574         throw std::string("Output path not found! Please set an existing output directory.");
575
576     mFSHelp.copyFile(mConfDir + "packages.tex", mOutPath + "packages.tex");
577     mFSHelp.copyFile(mConfDir + "doxygen.sty", mOutPath + "doxygen.sty");
578     mFSHelp.copyFile(mConfDir + "setup_doxygen.tex", mOutPath + "setup_doxygen.tex");
579     mFSHelp.copyFile(mConfDir + "codesettings.tex", mOutPath + "codesettings.tex");
580     mFSHelp.copyFile(mConfDir + "main.tex", mOutPath + "main.tex");
581     mFSHelp.copyFile(mConfDir + "docsettings.tex", mOutPath + "docsettings.tex");
582
583     // Öffne main.tex
584     std::ofstream mainFile;
585     mainFile.open( mOutPath + "main.tex", std::ios_base::app);
586     if(!mainFile.good())
587         throw std::string("Could not open main.tex");
588
589     // Füge subjec name hinzu
590     mainFile << "\\newcommand{\\headerleft}{" << mSubject <<"}" << std::endl;
591
592     // Füge Abteilug hinzu
593     mainFile << "\\newcommand{\\department}{" << mDepartment <<"}" << std::endl;
594
595     // Füge dokumentennamen hinzu
596     mainFile << "\\title{" << mDocName << "}" << std::endl;
597
598     mainFile << "\\date{\\today{}, " << mLocation << "}" << std::endl;
599
600     mainFile << "\\author{";
601     for(auto curAuthor : mAuthors)
602     {
603         mainFile << curAuthor->GetName();
604         if(curAuthor != *(mAuthors.end()-1))
605             mainFile << ", ";
606     }
607     mainFile << "}" << std::endl;
608
609
610     mainFile << std::endl;
611     mainFile << "\\input{codesettings.tex}" << std::endl;
612     mainFile << "\\begin{document}" << std::endl;
613     mainFile << "\\input{docsettings.tex}" << std::endl;
614     mainFile << std::endl;
615
616     // FÜge kapitel hinzu
617     for(auto curChap : mChapters)
618     {
619         curChap->WriteFile(mOutPath);
620         mainFile << "\t\\chapter{" << curChap->GetPrettyName() << "}" << std::endl;
621         mainFile << "\t\t\\input{" << curChap->GetOutFileName() << "}" << std::endl;
622         mainFile << std::endl;
623     }
624
625
626     //Füge Angabenseite hinzu
627     if(shouldAddCoverSheet())
628     {
629         // Öffne docsettings.tex
630         std::ofstream docSettingsFile;
631         docSettingsFile.open( mOutPath + "docsettings.tex", std::ios_base::app);
632         if(!docSettingsFile.good())
633             throw std::string("Could not open docsettings.tex");
634
635
636         docSettingsFile << "\\begin{titlepage}\n\t\\includepdf[pages={-},\n\t\tpicturecommand*={" << std::endl;
637         for(auto curAuthor : mAuthors)
638         {
639             docSettingsFile << "\t\t\\put(" << curAuthor->GetXNameCoordinate() << "," << curAuthor->GetYNameCoordinate() << "){" << curAuthor->GetName() << "}" << std::endl;
640             docSettingsFile << "\t\t\\put(" << curAuthor->GetXTimeEstimatedCoordinate() << "," << curAuthor->GetYTimeEstimatedCoordinate() << "){" << curAuthor->GetTimeEstimated() << "}" << std::endl;
641             docSettingsFile << "\t\t\\put(" << curAuthor->GetXTimeSpentCoordinate() << "," << curAuthor->GetYTimeSpentCoordinate() << "){" << curAuthor->GetTimeSpent() << "}" << std::endl;
642         }
643
644         docSettingsFile << "\t\t\\put(" << mDepartmentX << "," << mDepartmentY << "){" << mDepartment << "}" << std::endl;
645
646         docSettingsFile << "\t}]{coversheet.pdf}\n\\end{titlepage}" << std::endl;
647
648         docSettingsFile.close();
649     }
650
651     std::ofstream docSettingsFile;
652     docSettingsFile.open( mOutPath + "docsettings.tex", std::ios_base::app);
653     if(!docSettingsFile.good())
654         throw std::string("Could not open docsettings.tex");
655
656     // Hardcoded settings
657     docSettingsFile << "\\maketitle" << std::endl;
658     docSettingsFile << "\\tableofcontents" << std::endl;
659     docSettingsFile << "\\setcounter{tocdepth}{3}" << std::endl;
660     docSettingsFile << "\\newpage" << std::endl;
661
662     docSettingsFile.close();
663
664
665     if(shouldGenDoxygen())
666     {
667         mFSHelp.copyDir(mSourcePath, mOutPath + "src");
668         mFSHelp.copyFile(mConfDir + "doxyfile", mOutPath + "doxyfile");
669         mFSHelp.makeDir(mOutPath + "doxygen");
670
671         // Starte DoxyGen
672         std::vector<std::string> paramsDoxy = {mOutPath + "doxyfile"};
673         mFSHelp.executeCommand(mOutPath, "doxygen", paramsDoxy, mFSHelp.addDirLastSlash(mOutPath) + mLogFile);
674
675         mFSHelp.copyFile(mOutPath + "doxygen/doxygen.sty", mOutPath + "doxygen.sty");
676
677         std::vector<FSHelper::File> doxyFiles;
678         if(!mFSHelp.listFilesRecursive(mOutPath + "doxygen", doxyFiles, true))
679             throw std::string("Failed to get filenames of source files!");
680
681         // Nach class_ filtern
682         auto it = std::remove_if(doxyFiles.begin(),
683                                  doxyFiles.end(),
684                                  [](FSHelper::File x)
685         {
686
687             unsigned int ind = x.second.find_first_of("_");
688             if(ind ==  std::string::npos) return true; //Keine Dateiendung
689
690             // Daeien beginnend mit class_
691             if((x.second.substr(0, ind+1) == "class_"))
692                 return false;
693
694             // Alle anderen
695             return true;
696         });
697         doxyFiles.erase(it, doxyFiles.end());
698
699         // Nach namen sortieren
700         std::sort(doxyFiles.begin(), doxyFiles.end(),
701                   [](FSHelper::File left, FSHelper::File right)
702         {
703             return left.second < right.second;
704         }
705
706                  );
707
708         std::ofstream out;
709         out.open(mOutPath + "doxyfiles.tex", std::ios_base::out);
710         if(out.good())
711         {
712             out << "\\input{doxygen/hierarchy.tex}" << std::endl;
713             out << "\\input{doxygen/annotated.tex}" << std::endl;
714             for(auto curElem : doxyFiles)
715             {
716                 curElem.first.replace(curElem.first.find(mOutPath), mOutPath.length(), ""); //Erzeuge relativen Pfade
717                 out << "\\input{" << mFSHelp.convertPathToUnix(curElem.first) << curElem.second <<"}" << std::endl;
718             }
719         }
720         else
721             throw std::string("Failed to write doxygen chapter.");
722
723         out.close();
724
725         mainFile << "\t\\chapter{" << mDoxygenName << "}" << std::endl;
726         mainFile << "\t\t\\input{doxyfiles.tex}" << std::endl;
727         mainFile << std::endl;
728     }
729
730     // Füge Quellcode hinzu
731     if(shouldAddSource())
732     {
733         mFSHelp.copyDir(mSourcePath, mOutPath + "src");
734
735         std::vector<FSHelper::File> sourceFiles;
736         if(!mFSHelp.listFilesRecursive(mOutPath + "src", sourceFiles, true))
737             throw std::string("Failed to get filenames of source files!");
738
739
740
741         // Nach dateiendung filtern
742         auto it = std::remove_if(sourceFiles.begin(),
743                                  sourceFiles.end(),
744                                  [this](FSHelper::File x)
745         {
746
747             unsigned int ind = x.second.find_last_of(".");
748             if(ind ==  std::string::npos) return true; //Keine Dateiendung
749
750             // Dateiendung cpp oder h
751             if((x.second.substr(ind) == mSourceExtension) ||
752                (x.second.substr(ind) == mHeaderExtension))
753                 return false;
754
755             // Alle anderen
756             return true;
757         });
758         sourceFiles.erase(it, sourceFiles.end());
759
760         // Nach namen sortieren (header vor cpp)
761         std::sort(sourceFiles.begin(), sourceFiles.end(),
762                   [](FSHelper::File left, FSHelper::File right)
763                     {
764                         return left.second > right.second;
765                     }
766
767                  );
768
769         std::ofstream out;
770         out.open(mOutPath + "sourcefiles.tex", std::ios_base::out);
771         if(out.good())
772             for(auto curElem : sourceFiles)
773             {
774                 curElem.first.replace(curElem.first.find(mOutPath), mOutPath.length(), ""); //Erzeuge relativen Pfade
775                 out << "\\lstinputlisting[language=C++]{" << mFSHelp.convertPathToUnix(curElem.first) << curElem.second <<"}" << std::endl;
776             }
777         else
778             throw std::string("Failed to write sourcefiles chapter.");
779
780         out.close();
781
782         mainFile << "\t\\chapter{" << mSourceChapterName << "}" << std::endl;
783         mainFile << "\t\t\\input{sourcefiles.tex}" << std::endl;
784         mainFile << std::endl;
785     }
786
787     if(shouldAddCoverSheet())
788         mFSHelp.copyFile(mCoverSheet, mOutPath + "coversheet.pdf");
789
790     mFSHelp.copyDir(mConfDir + "resources", mOutPath + "resources");
791
792     mainFile << "\\end{document}" << std::endl;
793     mainFile.close();
794
795     //start dokument generierung
796     std::vector<std::string> paramsDryRun = {"-draftmode", "-interaction=nonstopmode", "main.tex"};
797     std::vector<std::string> paramsGen = {"-interaction=nonstopmode", "main.tex"};
798     mFSHelp.executeCommand(mOutPath, "pdflatex", paramsDryRun, mFSHelp.addDirLastSlash(mOutPath) + mLogFile);
799     mFSHelp.executeCommand(mOutPath, "pdflatex", paramsGen, mFSHelp.addDirLastSlash(mOutPath) + mLogFile);
800 }
801
802 void Model::saveTemplFile()
803 {
804     tinyxml2::XMLDocument outDoc;
805
806     tinyxml2::XMLNode * pDocSettings = outDoc.NewElement("docsettings");
807     tinyxml2::XMLNode * pChapters = outDoc.NewElement("chapters");
808
809     outDoc.InsertFirstChild(pDocSettings);
810     outDoc.InsertAfterChild(pDocSettings, pChapters);
811
812     // DocSettings
813     tinyxml2::XMLElement * pSubjName = outDoc.NewElement("subjectname");
814     tinyxml2::XMLElement * pLocation = outDoc.NewElement("location");
815     tinyxml2::XMLElement * pCoverSheet = outDoc.NewElement("cover_sheet");
816     pSubjName->SetText(mSubject.c_str());
817     pLocation->SetText(mLocation.c_str());
818     pCoverSheet->SetText(mHasCoverSheet);
819     pDocSettings->InsertEndChild(pSubjName);
820     pDocSettings->InsertEndChild(pLocation);
821     pDocSettings->InsertEndChild(pCoverSheet);
822
823     // Fileextensions
824     tinyxml2::XMLNode * pFileExtensions= outDoc.NewElement("fileextensions");
825     tinyxml2::XMLElement * pFileExtHeader = outDoc.NewElement("header");
826     tinyxml2::XMLElement * pFileExtSource= outDoc.NewElement("source");
827     if(mHeaderExtension.empty() || mSourceExtension.empty())
828         throw std::string("Please set header and source file extension to use!");
829
830     pFileExtHeader->SetText(mHeaderExtension.c_str());
831     pFileExtSource->SetText(mSourceExtension.c_str());
832     pFileExtensions->InsertEndChild(pFileExtHeader);
833     pFileExtensions->InsertEndChild(pFileExtSource);
834     pDocSettings->InsertEndChild(pFileExtensions);
835
836     // Doxygen
837     tinyxml2::XMLNode * pDoxyGen = outDoc.NewElement("doxygen");
838     tinyxml2::XMLElement * pDoxyEnabled = outDoc.NewElement("enabled");
839     tinyxml2::XMLElement * pDoxyChaptName = outDoc.NewElement("chaptername");
840     pDoxyEnabled->SetText(mDoxygen);
841     if(mDoxygen)
842         pDoxyChaptName->SetText(mDoxygenName.c_str());
843     else
844         pDoxyChaptName->SetText("Not set");
845     pDoxyGen->InsertEndChild(pDoxyChaptName);
846     pDoxyGen->InsertEndChild(pDoxyEnabled);
847     pDocSettings->InsertEndChild(pDoxyGen);
848
849     // Department
850     tinyxml2::XMLNode * pDepartment = outDoc.NewElement("department");
851     tinyxml2::XMLElement * pDepX = outDoc.NewElement("x_coor");
852     tinyxml2::XMLElement * pDepY = outDoc.NewElement("y_coor");
853     pDepX->SetText(mDepartmentX);
854     pDepY->SetText(mDepartmentY);
855     pDepartment->InsertEndChild(pDepX);
856     pDepartment->InsertEndChild(pDepY);
857     pDocSettings->InsertEndChild(pDepartment);
858
859     // Sourcecode
860     tinyxml2::XMLNode * pSource= outDoc.NewElement("sourcecode");
861     tinyxml2::XMLElement * pSrcEnabled = outDoc.NewElement("enabled");
862     tinyxml2::XMLElement * pSrcChaptName = outDoc.NewElement("chaptername");
863     pSrcEnabled->SetText(mSourceChapter);
864     if(mSourceChapter)
865         pSrcChaptName->SetText(mSourceChapterName.c_str());
866     else
867         pSrcChaptName->SetText("Not set");
868     pSource->InsertEndChild(pSrcEnabled);
869     pSource->InsertEndChild(pSrcChaptName);
870     pDocSettings->InsertEndChild(pSource);
871
872     // Speichere Kapitel zu XML
873     tinyxml2::XMLNode * pAuthors = outDoc.NewElement("authors");
874     for(auto curAuth: mAuthors)
875     {
876
877         tinyxml2::XMLNode * pAuthor = outDoc.NewElement("author");
878         tinyxml2::XMLElement * pNum = outDoc.NewElement("num");
879         tinyxml2::XMLElement * pNameX = outDoc.NewElement("x_coor");
880         tinyxml2::XMLElement * pNameY = outDoc.NewElement("y_coor");
881         tinyxml2::XMLNode * pTimeEst= outDoc.NewElement("time_estimated");
882         tinyxml2::XMLNode * pTimeSpent = outDoc.NewElement("time_spent");
883
884         tinyxml2::XMLElement * pTimeEstX = outDoc.NewElement("x_coor");
885         tinyxml2::XMLElement * pTimeEstY = outDoc.NewElement("y_coor");
886
887         tinyxml2::XMLElement * pTimeSpenX = outDoc.NewElement("x_coor");
888         tinyxml2::XMLElement * pTimeSpenY = outDoc.NewElement("y_coor");
889
890         pNum->SetText(curAuth->GetNum());
891         pNameX->SetText(curAuth->GetXNameCoordinate());
892         pNameY->SetText(curAuth->GetYNameCoordinate());
893         pTimeSpenX->SetText(curAuth->GetXTimeSpentCoordinate());
894         pTimeSpenY->SetText(curAuth->GetYTimeSpentCoordinate());
895         pTimeEstX->SetText(curAuth->GetXTimeEstimatedCoordinate());
896         pTimeEstY->SetText(curAuth->GetYTimeEstimatedCoordinate());
897
898         pAuthor->InsertEndChild(pNum);
899         pAuthor->InsertEndChild(pNameX);
900         pAuthor->InsertEndChild(pNameY);
901
902         pTimeEst->InsertEndChild(pTimeEstX);
903         pTimeEst->InsertEndChild(pTimeEstY);
904         pAuthor->InsertEndChild(pTimeEst);
905
906         pTimeSpent->InsertEndChild(pTimeSpenX);
907         pTimeSpent->InsertEndChild(pTimeSpenY);
908         pAuthor->InsertEndChild(pTimeSpent);
909
910         pAuthors->InsertEndChild(pAuthor);
911     }
912     pDocSettings->InsertEndChild(pAuthors);
913
914     // Speichere Kapitel zu XML
915     for(auto curChapt : mChapters)
916     {
917
918         tinyxml2::XMLNode * pChapter = outDoc.NewElement("chapter");
919         tinyxml2::XMLElement * pNum = outDoc.NewElement("num");
920         tinyxml2::XMLElement * pName = outDoc.NewElement("prettyName");
921         tinyxml2::XMLElement * pFileName = outDoc.NewElement("filename");
922         tinyxml2::XMLElement * pText = outDoc.NewElement("textTemplate");
923
924         pNum->SetText(curChapt->GetNum());
925         pName->SetText(curChapt->GetPrettyName().c_str());
926         pFileName->SetText(curChapt->GetOutFileName().c_str());
927
928         if(curChapt->GetText().empty())
929             pText->SetText("% Enter text here.");
930         else
931             pText->SetText(curChapt->GetText().c_str());
932
933         pChapter->InsertEndChild(pName);
934         pChapter->InsertEndChild(pNum);
935         pChapter->InsertEndChild(pFileName);
936         pChapter->InsertEndChild(pText);
937
938         pChapters->InsertEndChild(pChapter);
939     }
940
941     throwXmlError(outDoc.SaveFile(std::string(mOutPath + mSubject + ".xml").c_str()));
942 }
943
944 bool Model::shouldAddCoverSheet()
945 {
946     return mHasCoverSheet;
947 }
948
949 bool Model::shouldGenDoxygen()
950 {
951     return mDoxygen;
952 }
953
954 bool Model::shouldAddSource()
955 {
956     return mSourceChapter;
957 }
958
959 std::string Model::getSourceDir()
960 {
961     return mSourcePath;
962 }
963
964 std::string Model::getCovSheet()
965 {
966     return mCoverSheet;
967 }
968
969 void Model::setSourceDir(std::string path)
970 {
971     if(!mFSHelp.dirExists(path))
972         throw std::string("Source directory not found!");
973
974     mSourcePath = mFSHelp.addDirLastSlash(path);
975 }
976
977 void Model::setCovSheet(std::string file)
978 {
979     if(!mFSHelp.fileExists(file))
980         throw std::string("File not found!");
981     mCoverSheet = file;
982 }
983
984 std::string Model::getConfDir()
985 {
986     return mConfDir;
987 }
988
989 void Model::saveChaptersToFiles()
990 {
991     for(auto curChap : mChapters)
992         curChap->WriteFile(mOutPath);
993 }
994
995 bool Model::getGenerateEnable()
996 {
997     if(getNumChapters() == getNumFinishedChapters())
998     {
999
1000         if(shouldAddCoverSheet())
1001             if(!mFSHelp.fileExists(mCoverSheet))
1002                 return false;
1003
1004         if(shouldAddSource())
1005             if(!mFSHelp.dirExists(mSourcePath))
1006                 return false;
1007
1008         if(mDocName.empty())
1009             return false;
1010
1011         if(mDepartment.empty())
1012             return false;
1013
1014         return true;
1015     }
1016     return false;
1017 }
1018
1019 std::string Model::getSourceChapterName()
1020 {
1021     return mSourceChapterName;
1022 }
1023
1024 std::string Model::getDoxygenName()
1025 {
1026     return mDoxygenName;
1027 }
1028
1029 void Model::setAddCoverSheet(bool val)
1030 {
1031     mHasCoverSheet = val;
1032 }
1033
1034 void Model::setAddSource(bool val)
1035 {
1036     mSourceChapter = val;
1037 }
1038
1039 void Model::setGenDoxygen(bool val)
1040 {
1041     mDoxygen = val;
1042 }
1043
1044 void Model::setSourceChapterName(std::string name)
1045 {
1046     mSourceChapterName = name;
1047 }
1048
1049 void Model::setDoxygenName(std::string name)
1050 {
1051     mDoxygenName = name;
1052 }
1053
1054 std::string Model::getDocName()
1055 {
1056     return mDocName;
1057 }
1058
1059 void Model::setDocName(std::string name)
1060 {
1061     mDocName = name;
1062 }
1063
1064 std::string Model::getLocation()
1065 {
1066     return mLocation;
1067 }
1068
1069 std::string Model::getDepartment()
1070 {
1071     return mDepartment;
1072 }
1073
1074 void Model::setLocation(std::string location)
1075 {
1076     mLocation = location;
1077 }
1078
1079 void Model::setDepartment(std::string department)
1080 {
1081     mDepartment = department;
1082 }
1083
1084 int Model::getDepartmentX()
1085 {
1086     return mDepartmentX;
1087 }
1088
1089 int Model::getDepartmentY()
1090 {
1091     return mDepartmentY;
1092 }
1093
1094 void Model::setDepartmentX(unsigned int x)
1095 {
1096     mDepartmentX = x;
1097 }
1098
1099 void Model::setDepartmentY(unsigned int y)
1100 {
1101     mDepartmentY = y;
1102 }
1103
1104 std::string Model::getOutPath()
1105 {
1106     return mOutPath;
1107 }
1108
1109 std::string Model::getHeaderFileExtension()
1110 {
1111     return mHeaderExtension;
1112 }
1113
1114 std::string Model::getSourceFileExtension()
1115 {
1116     return mSourceExtension;
1117 }
1118
1119 void Model::setHeaderFileExtension(std::string fileExt)
1120 {
1121     if(fileExt.empty())
1122         throw std::string("Please set a header file extension!");
1123
1124     mHeaderExtension = fileExt;
1125 }
1126
1127 void Model::setLogFile(std::string log)
1128 {
1129     mLogFile = log;
1130 }
1131
1132 std::string Model::getLogFile()
1133 {
1134     return mLogFile;
1135 }
1136
1137 void Model::setSourceFileExtension(std::string fileExt)
1138 {
1139     if(fileExt.empty())
1140         throw std::string("Please set a source file extension!");
1141
1142     mSourceExtension = fileExt;
1143 }