Assignment_4_Doxygen_DavidLordan  2
Doxygen API for Assignment 4
 All Classes Files Functions Variables Enumerations Enumerator
main.cpp
Go to the documentation of this file.
1 /* File: main.cpp
2  * Author: David Lordan, UMass Lowell Computer Science
3  * david_lordan@student.uml.edu
4  *
5  * Created on October 11th, 2014, 5:20 PM, modified on Oct 23rd at 4:33 PM to
6  * add additional documentation.
7  *
8  * This program reads an XML file, then tokenizes and parses each line. As
9  * tags are opened, element objects are created and their pointers are pushed
10  * to a vector acting as a stack. During this process, a tree of elements is created
11  * and stored in memory. As the tags are closed, the element pointers
12  * are removed from the stack and the objects deleted. Depending on the choice of the user,
13  * as the program processes each line, the entire line, the current state of the XML document,
14  * tag name and content or attributes when applicable are all displayed, as well as
15  * any changes to the stack.
16  *
17  * After the tree has finished building and is stored in memory, the contents of the
18  * tree are displayed recursively. This includes displaying elements, their attributes,
19  * content and their child attributes.
20  */
21 
22 #include <cstdlib>
23 #include <string>
24 #include <fstream>
25 #include <iostream>
26 #include <vector>
27 #include <algorithm>
28 #include "Element.h"
29 
30 using namespace std;
31 
36 vector<Element*> elementVect;
37 
43 
50 
52 bool showProcessing = false;
53 
67 };
68 
76  cout << "ParserState = ";
77  switch (ps) {
78  case UNKNOWN: cout << "UNKNOWN";
79  break;
80  case STARTING_DOCUMENT: cout << "STARTING_DOCUMENT";
81  break;
82  case DIRECTIVE: cout << "DIRECTIVE";
83  break;
84  case ELEMENT_OPENING_TAG: cout << "ELEMENT_OPENING_TAG";
85  break;
86  case ELEMENT_CONTENT: cout << "ELEMENT_CONTENT";
87  break;
88  case ELEMENT_NAME_AND_CONTENT: cout << "ELEMENT_CONTENT";
89  break;
90  case ELEMENT_CLOSING_TAG: cout << "ELEMENT_CLOSING_TAG";
91  break;
92  case SELF_CLOSING_TAG: cout << "SELF_CLOSING_TAG";
93  break;
94  case STARTING_COMMENT: cout << "STARTING_COMMENT";
95  break;
96  case IN_COMMENT: cout << "IN_COMMENT";
97  break;
98  case ENDING_COMMENT: cout << "ENDING_COMMENT";
99  break;
100  case ONE_LINE_COMMENT: cout << "ONE_LINE_COMMENT";
101  break;
102  case ERROR: cout << "ERROR";
103  break;
104  default: cout << "UNKNOWN";
105  break;
106  }
107  cout << endl;
108 }
109 
130 ParserState parse(string currentLine, ParserState currentState, string &content, string &tagName) {
131 
132  //The function first determines if '>' is located in the current line, if not, the line must
133  //be a starting comment, in comment, or possibly unknown.
134  if (currentLine.find('>') == string::npos) {
135 
136  //The function then determines if the last line started a comment or
137  // was in a comment, if so, the current line is still in a comment.
138  if (currentState == STARTING_COMMENT || currentState == IN_COMMENT) {
139  return IN_COMMENT;
140  }
141 
142  //Checks to see if the line starts with '<!--' indicating a starting comment
143  if (currentLine[1] == '!' && currentLine[2] == '-' && currentLine[3] == '-') {
144  return STARTING_COMMENT;
145  }// If we do not have a '>' and the line is not a comment, parser returns
146  // unknown state.
147  else {
148  return UNKNOWN;
149  }
150  }//The following tests are for when a '>' is found.
151 
152 
153  //Checks if '>' is really '-->', implying an ending comment.
154  else if (currentLine[0] != '<'
155  && currentLine[currentLine.find('>') - 1] == '-'
156  && currentLine[currentLine.find('>') - 2] == '-') {
157 
158  return ENDING_COMMENT;
159 
160  // Continues, knowing that the '>' is not part of a closing comment.
161  } else {
162 
163  //The followings tests are if the first character of the line is '<'
164  if (currentLine[0] == '<') {
165 
166  //Checks if '<' is really '<?', implying a directive. The directive
167  //is extracted and DIRECTIVE returned.
168  if (currentLine[1] == '?') {
169  content = currentLine.substr(2, currentLine.rfind('?') - 2);
170 
171  return DIRECTIVE;
172 
173  //Checks for one line comment, uses the 'content' variable, which
174  //Is passed by reference, to store the extracted comment.
175  } else if (currentLine[1] == '!' && currentLine [2] == '-' && currentLine[3] == '-') {
176  content = currentLine.substr(4, currentLine.rfind('-') - 5);
177 
178  return ONE_LINE_COMMENT;
179 
180  //Checks for closing tag, extracts tag name.
181  } else if (currentLine[1] == '/') {
182  tagName = currentLine.substr(2, currentLine.find('>') - 2);
183  return ELEMENT_CLOSING_TAG;
184 
185  //Eliminating all other possibilities, the following now treats
186  // the current line as either an opening tag, or a complete element.
187  // Either way, the tag name is extracted and stored in tagName.
188  } else {
189 
190  //Extracting of tag name.
191  tagName = currentLine.substr(1, currentLine.find('>') - 1);
192  tagName = tagName.substr(0, tagName.find(' '));
193 
194  //Checks for complete element, extracts the content
195  if (currentLine[currentLine.rfind('<') + 1] == '/') {
196  content = currentLine.substr(currentLine.find('>') + 1, currentLine.find('/'));
197  content = content.substr(0, content.find('/') - 1);
199  }// Checks for self-closing tag extracts content.
200  else if (currentLine[currentLine.rfind('>') - 1] == '/') {
201  content = ("{EMPTY}");
202  return SELF_CLOSING_TAG;
203  }// By elimination the line must be an opening tag, whos tag name
204  // has already been extracted.
205  else {
206 
207  return ELEMENT_OPENING_TAG;
208  }
209  }
210  }// If for any reason the current line did not pass any of the above tests, an
211  //unknown state is returned.
212  else {
213  return UNKNOWN;
214  }
215  }
216 }
217 
226 string trim(string str) {
227  string::iterator it = str.begin();
228  while (*it == ' ' || *it == '\t') {
229  str.erase(str.begin());
230  }
231  it = str.end() - 1;
232  while (*it == ' ' || *it == '\t') {
233  str.erase(str.end() - 1);
234  }
235  return str;
236 }
237 
243 void showStack(vector<Element*> elementVect) {
244  cout << "*** The stack now contains: ";
245 
246  if (!elementVect.empty()) {
247  for (vector<Element*>::iterator it = elementVect.begin(); it != elementVect.end(); ++it) {
248  cout << (*it)->getTagName();
249 
250  // Simply adds a period at the end of the list, otherwise the element
251  // names are separated with a comma.
252  if (it == elementVect.end() - 1)
253  cout << ".";
254  else
255  cout << ", ";
256  }
257  cout << endl;
258  } else {
259  cout << "{EMPTY}" << endl;
260  }
261 }
262 
272 void proccesLine(string currentLine, ParserState &currentState, int lineNumber, string content, string tagName) {
273 
274 
275  // Depending upon the current state, an appropriate output is displayed,
276  // and the proper actions are taken with the stack.
277  switch (currentState) {
278 
279  case STARTING_COMMENT:
280  {
281  if (showProcessing) {
282  cout << lineNumber << ": " << currentLine << endl;
283  cout << "*** Comment started";
284  }
285  break;
286  }
287  case IN_COMMENT:
288  {
289  if (showProcessing) {
290  cout << lineNumber << ": " << currentLine << endl;
291  cout << "*** Comment line";
292  }
293  break;
294  }
295 
296  case ENDING_COMMENT:
297  {
298  if (showProcessing) {
299  cout << lineNumber << ": " << currentLine << endl;
300  cout << "*** Ending comment";
301  }
302  break;
303  }
304  case ONE_LINE_COMMENT:
305  {
306  if (showProcessing) {
307  cout << lineNumber << ": " << content << endl;
308  cout << "*** One line comment";
309  }
310  break;
311  }
312 
313  case DIRECTIVE:
314  {
315  if (showProcessing) {
316  cout << lineNumber << " : " << currentLine << endl;
317  cout << "*** Directive = " << content << endl;
318  }
319  break;
320  }
321 
322  case ELEMENT_OPENING_TAG:
323  // If the current line is determined to be an opening tag, a new element
324  // object is created and its pointer pushed to the stack. The stack contents
325  // are then listed to confirm the change.
326  {
327 
328  Element* ElementPtr = new Element(tagName, lineNumber, "Empty", currentLine);
329 
330  // The new element is then listed a child of the current element.
331  currentElement->addChild(ElementPtr);
332 
333  // The current element is then set as the parent to the new element.
334  ElementPtr->setParent(currentElement);
335 
336  // The new element is added to the element stack.
337  elementVect.push_back(ElementPtr);
338 
339  // Tree building process is displayed to the user.
340  if (showProcessing) {
341  cout << lineNumber << " : " << currentLine << endl;
342  cout << "*** Element Opened = " << tagName << endl;
343 
344  if (currentElement != emptyRoot) {
345  cout << "*** Creating new child: " << ElementPtr->getTagName()
346  << ", for parent: " << currentElement->getTagName() << endl;
347  } else {
348  cout << "*** No parent found, creating root element: " << ElementPtr->getTagName() << endl;
349  }
350 
351  if (!ElementPtr->vecAttribute.empty()) {
352  cout << "*** Attributes found: " << endl;
353  ElementPtr->displayAttributes();
354  }
355 
357 
358  }
359  // Sets the level of the new element to be one level higher than its parent.
360  ElementPtr->setLevel(ElementPtr->getParent()->getLevel() + 1);
361 
362  // Sets the current element as the new element, building a new branch of the tree.
363  currentElement = ElementPtr;
364  break;
365  }
366 
368  {
369 
370  // Creates a new element and creates the parent/child relationship with the current element.
371  Element* ElementPtr = new Element(tagName, lineNumber, content, currentLine);
372  currentElement->addChild(ElementPtr);
373  ElementPtr->setParent(currentElement);
374 
375  if (showProcessing) {
376  cout << lineNumber << " : " << currentLine << endl;
377  cout << "*** Complete element found:" << endl;
378  cout << "*** Element Name = " << tagName << endl;
379  cout << "*** Element Content = " << content << endl;
380  cout << "*** Creating new child: " << ElementPtr->getTagName()
381  << ", for parent: " << currentElement->getTagName() << endl;
382 
383  if (!ElementPtr->vecAttribute.empty()) {
384  cout << "*** Attributes found: " << endl;
385  ElementPtr->displayAttributes();
386  }
387  cout << "*** Stack unchanged" << endl;
388 
389  }
390  // Sets the new element's level to be one higher than its parent.
391  ElementPtr->setLevel(ElementPtr->getParent()->getLevel() + 1);
392  break;
393  }
394 
395  case ELEMENT_CLOSING_TAG:
396 
397  // When a closing tag is found, if it does not match the top tag name
398  // on the stack then there is a problem with the XML. The current state
399  // is set to ERROR, which will end the program due to the condition of
400  // the while loop in the 'openFile' function.
401  {
402  if (showProcessing) {
403  cout << lineNumber << " : " << currentLine << endl;
404  }
405 
406  if (tagName != elementVect[elementVect.size() - 1]->getTagName()) {
407  cout << "ERROR!!! - Closing tag on line " << lineNumber
408  << " does not match last opened tag, '"
409  << elementVect[elementVect.size() - 1]->getTagName() << "' ." << endl;
410  ;
411  cout << "XML is not well formed. Ending program." << endl;
412  currentState = ERROR;
413  }// If the closing tag does match the top element on the stack, the element
414  // is popped and the updated state of the stack is displayed to confirm
415  // the change.
416  else {
417 
418  // Sets the current element to be it's parent, moving up the tree.
419  currentElement = currentElement->getParent();
420 
421  elementVect.pop_back();
422 
423  if (showProcessing) {
424  cout << "*** Element closed = " << tagName << endl;
426  }
427  }
428 
429  break;
430  }
431 
432  case SELF_CLOSING_TAG:
433  {
434 
435  // Creates a new element and sets up the parent/child relationship with the current element.
436  Element* ElementPtr = new Element(tagName, lineNumber, "Empty", currentLine);
437  currentElement->addChild(ElementPtr);
438  ElementPtr->setParent(currentElement);
439 
440  if (showProcessing) {
441  cout << lineNumber << " : " << currentLine << endl;
442  cout << "*** Self-closing element found: " << endl;
443  cout << "*** Element Name = " << tagName << endl;
444  cout << "*** Element Content = " << content << endl;
445  cout << "*** Creating new child: " << ElementPtr->getTagName()
446  << ", for parent: " << currentElement->getTagName() << endl;
447 
448  if (!ElementPtr->vecAttribute.empty()) {
449  cout << "*** Attributes found: " << endl;
450  ElementPtr->displayAttributes();
451  }
452  cout << "*** Stack unchanged." << endl;
453  }
454 
455  // Sets the new element's level to be one level higher that it's parent.
456  ElementPtr->setLevel(ElementPtr->getParent()->getLevel() + 1);
457  break;
458  }
459 
460  case UNKNOWN:
461  {
462  cout << currentLine << endl;
463  cout << "Parser state unknown!" << endl;
464  cout << "XML is not well formed. Ending program." << endl;
465  break;
466  }
467 
468  default:
469  {
470  cout << currentLine << endl;
471  cout << "ERROR!!!" << endl;
472  cout << "XML is not well formed. Ending program." << endl;
473  break;
474  }
475  }
476 
477  if (showProcessing) {
478  cout << endl << endl;
479  }
480 }
481 
491 int openfile(string strPath) {
492 
493  // Variable to store the current state of the document, which will be used
494  // to parse and process each line. Initialized to STARTING_DOCUMENT.
495  ParserState currentState = STARTING_DOCUMENT;
496 
497  //Creates an object of the class ifstream and uses it to open the passed
498  // file name.
499  ifstream infile;
500  infile.open(strPath);
501 
502  //Initializes a string to store the current line being processed and
503  //initializes a line number counter.
504  string currentLine;
505  int lineNumber = 1;
506 
507  //The entire top line of the passed file is removed and stored in the
508  //variable "currentLine".
509  getline(infile, currentLine);
510 
511  // These strings are used to store content and tag names extracted by the
512  // 'currentState' function, which are then passed into the 'proccessLine'
513  // function, and then in turn passed to the Element constructor.
514  string content;
515  string tagName;
516 
517  cout << "Building tree for file: " << strPath << "..." << endl << endl;
518 
519  // This loop scans through and processes the passed file name line by line
520  // until the last line has been reached. This will also terminate if the
521  // current state is ERROR or UNKNOWN, which indicates that a passed XML file is not
522  // well-formed.
523  while (!currentLine.empty() && currentState != ERROR && currentState != UNKNOWN) {
524 
525  //Passes the current line the trim function, removing any white space
526  // and/or tabs in the line.
527  currentLine = trim(currentLine);
528 
529  // The current state is determined by the state returned from the previous
530  // line and the text on the current line. 'Content' and 'tagName' are
531  // passed to store extracted content and tag names.
532  currentState = parse(currentLine, currentState, content, tagName);
533 
534  // Processes the line depending upon the current state, displays relevant
535  // information and updates the element pointer stack, 'elementVect' .
536  proccesLine(currentLine, currentState, lineNumber, content, tagName);
537 
538  //Increments the line number, gets the next line of the file.
539  lineNumber++;
540  getline(infile, currentLine);
541  }
542  //Closes the input file.
543  infile.close();
544 
545  // If after reading the XML file the current state is "ERROR", the function
546  // returns the appropriate exit status.
547  if (currentState == ERROR || currentState == UNKNOWN) {
548  return EXIT_FAILURE;
549  } else {
550  return EXIT_SUCCESS;
551  }
552 
553 }
554 
560 void clearVectorContents(vector <Element*> &vect) {
561 
562  for (vector<Element*>::iterator it = vect.begin(); it != vect.end(); ++it) {
563  delete *it;
564  }
565  vect.clear();
566 }
567 
573 void displayTree(Element* currentElement) {
574 
575  // Begins tree output.
576  if (currentElement != emptyRoot) {
577 
578  for (int i = 1; i < currentElement->getLevel(); i++) {
579  cout << ".";
580  }
581  cout << "Element \"" << currentElement->getTagName()
582  << "\" was found at line " << currentElement->getLineNo()
583  << ", level " << currentElement->getLevel();
584  if (currentElement->getStrContent() == "Empty")
585  cout << ", with no content";
586  else
587  cout << ", containing \"" << currentElement->getStrContent() << '"';
588  cout << " and " << currentElement->getNoOfChildren();
589  if (currentElement->getNoOfChildren() == 1)
590  cout << " child";
591  else
592  cout << " children";
593 
594  if (currentElement->hasAttributes()) {
595 
596  cout << ", along with " << currentElement->vecAttribute.size();
597 
598  if (currentElement->vecAttribute.size() == 1)
599  cout << " attribute: " << endl;
600  else
601  cout << " attributes: " << endl;
602 
603  currentElement->displayAttributes();
604  } else
605  cout << "." << endl;
606 
607  cout << endl;
608  }// If the current element is the emptyRoot, which should be the parameter the first time this
609  // function is called, then the user is told that the element tree has started to be displayed.
610  else {
611  //Displays a line of '*' to separate the display of the tree building process
612  for (int i = 0; i < 40; i++) {
613  cout << "*";
614  }
615  cout << endl << endl << "Now showing the element tree:" << endl << endl;
616  }
617 
618  // Checks if the current element has children, if so, the program iterates through
619  // each child to calling this function for each.
620  if (!currentElement->vecChildren.empty()) {
621 
622  for (vector<Element*>::iterator it = currentElement->vecChildren.begin();
623  it != currentElement->vecChildren.end(); ++it) {
624 
625  displayTree(*it);
626 
627  }
628  }
629 }
630 
635 void deleteTree(Element* currentElement) {
636 
637  if (!currentElement->vecChildren.empty()) {
638 
639  for (vector<Element*>::iterator it = currentElement->vecChildren.begin();
640  it != currentElement->vecChildren.end(); ++it) {
641 
642  deleteTree(*it);
643  }
644  }
645  delete(currentElement);
646  currentElement->vecChildren.clear();
647 }
648 
653 void askFormat() {
654  int input = 0;
655 
656  while (input != 1 && input != 2 && cin) {
657  cout << "Would you like to see the tree as it is being built?" << endl;
658  cout << "Enter 1 for yes or 2 for no:" << endl;
659  cin>>input;
660 
661  switch (input) {
662  case 1:
663  {
664  showProcessing = true;
665  break;
666  }
667  case 2:
668  {
669  showProcessing = false;
670  break;
671  }
672  default:
673  {
674  cout << "Invalid entry, please try again." << endl;
675  cin.clear();
676  cin.ignore(256, '\n');
677  break;
678  }
679  }
680  }
681 }
682 
691 void writeHTML(Element* currentElement, ofstream &os) {
692 
693  if (currentElement == emptyRoot) {
694 
695  // If the current element is the root, standard opening code for an html file is written.
696  os << "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n <title></title>\n"
697  " <meta charset=\"utf-8\">\n</head>\n<body>\n";
698 
699  }
700  if (currentElement != emptyRoot) {
701  os << "<p>";
702  os << "<div>";
703  for (int i = 1; i < currentElement->getLevel(); i++) {
704  os << ".";
705  }
706  os << "Element \"" << currentElement->getTagName()
707  << "\" was found at line " << currentElement->getLineNo()
708  << ", level " << currentElement->getLevel();
709  if (currentElement->getStrContent() == "Empty")
710  os << ", with no content";
711  else
712  os << ", containing \"" << currentElement->getStrContent() << '"';
713  os << " and " << currentElement->getNoOfChildren();
714  if (currentElement->getNoOfChildren() == 1)
715  os << " child";
716  else
717  os << " children";
718 
719  if (currentElement->hasAttributes()) {
720 
721  os << ", along with " << currentElement->vecAttribute.size();
722 
723  if (currentElement->vecAttribute.size() == 1)
724  os << " attribute: ";
725  else
726  os << " attributes: ";
727 
728  for (int i = 0; i < currentElement->vecAttribute.size(); i++) {
729  os << "</div>";
730  os << "<div>";
731  for (int j = 1; j < currentElement->getLevel(); j++) {
732  os << ".";
733  }
734 
735  os << "Attribute name: " << currentElement->vecAttribute[i]->getAttName();
736  os << ", Attribute value: " << currentElement->vecAttribute[i]->getAttValue();
737  os << "</div>";
738  }
739 
740  } else {
741  os << ".";
742  os << "</div>";
743  }
744 
745  os << "</p>" << endl;
746  }
747 
748  // Checks if the current element has children, if so, the program iterates through
749  // each child to calling this function for each.
750  if (!currentElement->vecChildren.empty()) {
751 
752  for (vector<Element*>::iterator it = currentElement->vecChildren.begin();
753  it != currentElement->vecChildren.end(); ++it) {
754 
755  writeHTML(*it, os);
756  }
757  }
758 
759  // After the function has returned to the empty root, the ofstream is closed.
760  if (currentElement == emptyRoot) {
761  os << "</body>";
762  os.close();
763  }
764 }
765 
772 int main(int argc, char** argv) {
773 
774  // Asks the user if they would like the tree building process to be displayed.
775  askFormat();
776 
777  // If the XMl file was opened successfully, the element tree will be displayed and then deleted.
778  if (openfile("Assignment4_musicFile.xml") == EXIT_SUCCESS) {
779  displayTree(currentElement);
780 
781  /* ofstream object which is created by the 'openHTML' function. This is done to avoid
782  * creating a global ofstream object. */
783  ofstream htmlStream;
784  htmlStream.open("HTML_OUTPUT.html");
785 
786  //Writes the element tree to an html file.
787  writeHTML(currentElement, htmlStream);
788 
789  deleteTree(currentElement);
790  }
791  // Clears any remaining contents of the 'elementVect' vector, deleting each
792  // object that any pointers in the vector may point to.
794 
795  return 0;
796 }
Definition: main.cpp:62
Element * getParent()
Definition: Element.cpp:224
int openfile(string strPath)
Definition: main.cpp:491
Element * currentElement
Definition: main.cpp:42
bool hasAttributes()
Definition: Element.cpp:263
int main(int argc, char **argv)
Definition: main.cpp:772
void writeHTML(Element *currentElement, ofstream &os)
Definition: main.cpp:691
ParserState parse(string currentLine, ParserState currentState, string &content, string &tagName)
Definition: main.cpp:130
string getStrContent() const
Definition: Element.cpp:95
vector< Attribute * > vecAttribute
Definition: Element.h:163
Definition: main.cpp:66
int getLineNo() const
Definition: Element.cpp:86
void setLevel(int newLevel)
Definition: Element.cpp:253
void setParent(Element *Parent)
Definition: Element.cpp:215
void deleteTree(Element *currentElement)
Definition: main.cpp:635
vector< Element * > vecChildren
Definition: Element.h:167
bool showProcessing
Definition: main.cpp:52
void ShowState(ParserState ps)
Definition: main.cpp:75
string getTagName() const
Definition: Element.cpp:77
string trim(string str)
Definition: main.cpp:226
void displayAttributes()
Definition: Element.cpp:122
int getNoOfChildren()
Definition: Element.cpp:233
void displayTree(Element *currentElement)
Definition: main.cpp:573
vector< Element * > elementVect
Definition: main.cpp:36
Element * emptyRoot
Definition: main.cpp:49
void askFormat()
Definition: main.cpp:653
void showStack(vector< Element * > elementVect)
Definition: main.cpp:243
ParserState
Definition: main.cpp:61
void clearVectorContents(vector< Element * > &vect)
Definition: main.cpp:560
void proccesLine(string currentLine, ParserState &currentState, int lineNumber, string content, string tagName)
Definition: main.cpp:272
int getLevel()
Definition: Element.cpp:243
void addChild(Element *newChild)
Definition: Element.cpp:204