Question
Problem Definition In this assignment, you will be completing the bulletin board you began in PROGRAM 3. We will be adding two major new functionalities:
Problem Definition
In this assignment, you will be completing the bulletin board you began in PROGRAM 3.
We will be adding two major new functionalities:
The bulletin board will now be able to handle replies to replies, and will display them indented according to their "level" (e.g. Replies are indented 2 spaces; Replies to Replies 4 spaces; Replies to Replies to Replies 6 spaces; etc.) You will use recursive techniques to display this.
The board will now be able to save the entire board (all Message content) to file, and read that file back in; as well as reading in User data from file as you did with the previous bulletin board assignment.
Class Specifications
BBoard Class interface
You may not change the public interface or the encapsulated data in any way; you may modify or omit any of the suggested private "helper" functions, or add new ones.
Private Data Fields
title - string : title of the board; initialized via constructor
userList - vector : list of members; initially empty, then populated via loadUsers()
currentUser (Major change from previous assignment!) - const User * : this is now a pointer to the user who is currently logged in; it is initialized by the constructors to 0 (nullptr), then set via login() REMEMBER to change all your currentUser.getUsername() to currentUser->getUsername()
messageList (Major change from previous assignment!) - vector : list of message pointers, initially empty
Public Member Functions
This is the public interface of the class - the contract with the user.
BBoard( )
default constructor that creates a board with a default title, empty user & message lists, and a no current user (what should pointer be set to if there is nothing to point to?)
BBoard(const string &ttl)
same as default c'tor, except that it sets the title of the board.
~BBoard( )
destructor that deallocates all Messages pointed to by messageList
(THINK CAREFULLY ABOUT THIS!!)
bool loadUsers(const string &userfile)
This function gets a filename (userfile) of a file that stores the user info in the given format (See Input File Format Specs).
It opens and reads the file of all authorized users and passwords, constructs a User object from each name/password pair, and populates the userList vector.
If an error occurs when opening the file, it returns false. Returns true otherwise.
bool loadMessages(const string &datafile) New !!!
This function gets a filename (datafile) of a file that stores the messages from previous sessions in the given format (See File Format Specs).
It opens and reads the file, creating Topic and Reply objects as appropriate, and fills the messageList vector (note: remember that messageList is a vector of Message pointers, not Messages).
If an error occurs when opening the file, it returns false. Returns true otherwise.
See below (Tips and Tricks) for tips.
bool saveMessages(const string &datafile) New !!!
This is the final action of the bulletin board before closing:
It gets a filename (datafile) of a file that will store all of the the messages, and after opening the file writes the messages into it with the same format (See File Format Specs).
If an error occurs when opening the file, it returns false. Returns true otherwise.
(Note: Since you will be opening the file for writing - i.e. an ofstream - "file not found" is NOT an error; a new file will simply be created for you).
See below (Tips and Tricks) for tips.
void login( ) (Major changes from previous assignment!)
asks for and validates current user/password
This function asks for a username and password, and checks the userList vector for a matching User.
If a match is found, it sets currentUser to the identified User from the list (remember, currentUser is now apointer, not an actual object).
If not found, it will keep asking until a match is found or the user types: 'q' or 'Q' as the username (you may assume we do not have a member with name 'q' or 'Q')
When the user chooses to quit, say "Bye!" and just return from the login function - meaning that in main(), currentUser will be unchanged from its initial value, which should have been set to 0 or nullptr.
See the output specifications for details.
void run( ) (No direct change, but see the private display() and addReply() helper functions)
This function includes the main loop of the BBoard.
If and only if there is a valid currentUser, enter the main loop, displaying the menu items:
Display Messages ('D' or 'd')
Add New Topic('N' or 'n')
Add Reply ('R' or 'r')
Quit ('Q' or 'q')
The user should select one of these menu items, which should then invoke the corresponding method of BBoard; with any other input, the user should be asked to try again.
'Q'/'q' should re-set currentUser to 0 (or nullptr) and then end the run function (return).
Note: if login() did not set a valid currentUser, this function must immediately return without showing the menu - i.e. there must be no way to get around logging in!
See suggested private functions for more details.
Suggested Private Member Functions
These are "helper functions": member functions that will never be needed by a user of the class - they are needed by the public interface functions. You are free to change/add/remove these functions as you wish:
void addUser(const string &name, const string &pass)
This function may be called from the loadUsers function to add the users from file to userList.
bool user_exists(const string& name, const string& pass) const (No longer needed)
const User * getUser(const string &name, const string &pw) const
This function includes the functionality of the old "user_exists" helper:
It traverses the userList, testing for the existence of a User with the given name and password; if this User is NOT found, the function returns 0 (or nullptr); otherwise it returns a pointer to the identified User
(the statement return &userList.at(i) will work - make sure you understand why!!)
This function may be used by login() to set the currentUser (which is now, obviously, a pointer, not an object).
There must be no other way to set currentUser to anything other than 0 (or nullptr).
void display() const Major Change !!!
This function will traverse the BBoard's message list, and invoke the print function on Topic objects ONLY.
It will then be the responsibility of the Topic object to invoke the print function recursively on its own replies.
The BBoard display function will ignore all Reply objects in its message list.
Question: how will BBoard know which Messages are Topics; and how will it know what argument to pass into the print function?
void add_message() (No longer needed)
void addTopic() New !!!
This function asks the user to create a new Topic (i.e. the first message of a new discussion "thread"). Every Topic includes a subject (single line), and a body that may consist of multiple lines. e.g.,
Subject: "Thanks" Body: "I would like to thank you for this awesome board. I'll visit here regularly."
The body ends when the user enters an empty line (i.e. a "double enter"). Each Topic also stores the username of currentUser; and a message id, which is (index of its Message* + 1)
For example, the first message on the board will be a Topic whose pointer will be stored at index 0 of the messageList vector, so its message id will be (0 + 1) = 1
(there are better ways of establishing unique ids for a set of objects, but for now this will work fine)
Once the Topic has been constructed, a pointer to it is added to messageList.
Hint: Do you need pointers to automatic or dynamic Topics?
void addReply() New !!!
This function asks the user to enter a reply to a given Message (which may be either a Topic or a Reply, so we can handle nested replies).
The addReply function first asks the user for the id of the Message to which they are replying; if the number provided is greater than the size of messageList it should output an error message and loop back, continuing to ask for a valid Message id number until the user enters either -1 (or any negative number, to return to the menu); or a valid id.
If the id is valid, then the function asks for the body of the new message, and constructs the Reply, pushing back a pointer to it on the messageList
The subject of the Reply is a copy of the parent Topic's subject with the "Re: " prefix.
e.g., suppose the subject of message #9 was "Thanks", and a user is replying to that message:
Enter Message ID: 9 Body: It was a pleasure implementing this. I hope everyone likes it.
Note: As before, the body ends when the user enters an empty line.
The above dialog will generate a reply that has "Re: Thanks" as its subject and "It was a pleasure implementing this. I hope everyone likes it." as its body.
How will we know what Topic this is a reply to?
In addition to keeping a pointer to every Message (whether Topic or Reply) in BBoard's messageList vector, every Message (whether Topic or Reply) will also store a vector of pointers to all its own Replies.
So whenever we build a Reply, we must immediately store a pointer to it inside its parent Message. Therefore, you will:
create a dynamic Reply with the input data. The Reply's constructor should set the Reply's subject to "Re: " + its parent's subject.
call the addChild function on the parent message to push_back the Message* (to the new Reply) to the parent's childList vector;
Finally, push_back the same pointer to BBoard's messageList.
Note: When the user chooses to return to the menu, do not call run() again - just return from this addReply function.
User Class
The User class is unchanged from the previous assignment - just make sure your implementation works correctly. As always, you may not alter the class' public interface!
Message, Topic and Reply Classes
We are going to promote our original Message class to an abstract base class (it has two pure virtual functions, and thus can never be instantiated), and derive two new classes from it: Topic and Reply.
The Topic class works like the previous Message, holding the first posting of a thread, with Reply objects continuing the discussion.
The Reply class will be able to manage nested replies, i.e. "Replies to Replies" (to any level), using the technique of recursion.
ID: The "id" of all Topic and Reply objects is just their "number" - i.e. their (index in messageList + 1). This means that the ids of Replies may not be contiguous with their parent:
e.g. two Replies to message id #9 might be numbered #34 and #61
Implement the Message, Topic and Reply classes according to the following interfaces:
As always, you may not alter any PUBLIC member functions, or PRIVATE/PROTECTED member variables
Message.h
//inclusion guards //includes class Message { // abstract base class
// protected will allow access to these members by objects of derived classes protected: string author; string subject; string body; unsigned id; // New !! // This will be the size of the messageList vector to which the // newly constructed Message * is being pushed_back vector childList; // New !! // This is how a Message is able to keep track of its Replies public: // default constructor Message(); // Parameterized constructor;
// id will be the size of current BBoard's messageList Message(const string &athr,
const string &sbjct,
const string &body,
unsigned id); // Be very careful here: some Messages will have two pointers to
// them, stored in very different places! // Where are they, and who should be responsible for deleting
// them? virtual ~Message(); // returns true if the object is a Reply. virtual bool isReply() const = 0; virtual string toFormattedString() const = 0; // New!! /* This function is responsible for printing the Message * (whether Topic or Reply), and and all of the Message's
* "subtree" recursively: * After printing the Message with indentation n and appropriate
* format (see output details), it will invoke itself
* recursively on all of the Replies in its childList, * incrementing the indentation value at each new level. * * Note: Each indentation increment represents 2 spaces. e.g. if * indentation == 1, the reply should be indented 2 spaces, if
* it's 2, indent by 4 spaces, etc. */ void print(unsigned indentation) const; // New !! //returns the subject string. const string & getSubject() const; // returns the ID. unsigned getID() const; // New !! // Adds a pointer to the child to the parent's childList. void addChild(Message *child); // New !! }; //end inc guards
Topic.h
//inclusion guards //includes class Topic : public Message { // no new member variables public: //default constructor Topic(); //Parameterized constructor Topic(const string &athr,
const string &sbjct,
const string &body,
unsigned id); virtual bool isReply() const; // Returns false /* toFormattedString writes the contents of the Topic to a * string in the following format: :id: 4 :subject: single line :from: author :children: 5 6 [this line should not be printed if there are no children.] :body: multiple lines - line 1 line 2 line 3 * line starting with :children: has the ID's of all children (See file specs. * for details)
* This function should not assume the last character in body is a newline.
* In other words, this function must manually add a newline after the last
* line of the body (line 3 in this example).
* Also, the last character in the string must be a newline. */ virtual string toFormattedString() const; // New !! }; //end inc guards
Reply.h
//inclusion guards //includes class Reply : public Message { // no new member variables public: //default constructor Reply(); /* Parameterized constructor - similar to Message's constructor except: * The subject should be initialized to "Re: " not . */ Reply(const string &athr,
const string &sbjct,
const string &body,
unsigned id); virtual bool isReply() const; // Returns true /* toFormattedString writes the contents of the Reply to a string in the
* following format: :id: 4 :subject: single line :from: author :children: 5 6 [this line should not be printed if there are no children.] :body: multiple lines 2nd line * the line starting with :children: has the list of ID's of all children
* (See file specs. for details)
* This function should not assume the last character in body is a newline.
* In other words, this function must manually add a newline after the last
* line of the body (line 3 in this example).
* Also, the last character in the string must be a newline. */ virtual string toFormattedString() const; // New !! }; //end inc guards
Main function that should be used for the compare output tests. As before, you should create a very different main function when unit testing your member functions.
//includes int main() { string userfile; string datafile;
cout << "User file?" << endl;
cin >> userfile;
cout << endl;
cout << "Message file?" << endl;
cin >> datafile;
cout << endl;
BBoard bb("CS12 Bulletin Board"); // load users from file if (!bb.loadUsers(userfile)) { cout << "ERROR: Cannot load users from " << userfile << endl; return 1; } // load messages if (!bb.loadMessages(datafile)) { cout << "ERROR: Cannot load messages from " << datafile << endl; return 1; } bb.login(); bb.run(); // save messages if (!bb.saveMessages(datafile)) { cout << "ERROR: Cannot save messages to " << datafile << endl; return 1; } return 0; }
Message File Specifications
The format specifications of the Message file:
numberofmessages :id: 1 :subject: single line :from: author :body: multiple lines line 2 :id: 2 :subject: single line :from: author :children: 3 4 //This line is omitted if there are no child //messages. See first and last blocks. :body: line 1 line 2 line 3 ... ... :id: 4 :subject: single line :from: author :body: line1
sample Message file you can use when testing your loadMessages function and overall program:
10 :id: 1 :subject: CS12 Assignment 7 :from: messi :children: 6 9 :body: The assignment is hard so go step by step. You can read the Tips & Tricks part for some help.
Has anyone started already? :id: 2 :subject: Your favorite TV show :from: ricq7 :children: 3 5 :body: So guys, what is your favorite tv show? :id: 3 :subject: Re: Your favorite TV show :from: mike :children: 4 :body: Game of Thrones is an awesome show but there are too
many characters to remember. We even did not see half of the families and still at least 20
important characters are introduced already. Is there anyplace that I can read about the characters? :id: 4 :subject: Re: Re: Your favorite TV show :from: ricq7 :children: 8 :body: Well the book is one hell of a resource. And, the script is written very loyal to the text. :id: 5 :subject: Re: Your favorite TV show :from: ali87 :children: 7 :body: Whose line is it anyway!!! Too bad, it is no more. :id: 6 :subject: Re: CS12 Assignment 7 :from: messi :body: BUMP :id: 7 :subject: Re: Re: Your favorite TV show :from: jenny :body: The cat! Colin Mochrie is hilarious. :id: 8 :subject: Re: Re: Re: Your favorite TV show :from: mike :body: I'll check that. Thanks. :id: 9 :subject: Re: CS12 Assignment 7 :from: messi :body: BUMP :id: 10 :subject: Towel Day :from: messi :body: Bring your towels on May 25. See here: http://www.towelday.org/
Output Specifications
Sample output:
Welcome to Jack's Amazing Bulletin Board Enter your username ('Q' or 'q' to quit): muzo Enter your password: cs12 Invalid Username or Password! Enter your username('Q' or 'q' to quit): ali87 Enter your password: 8422 Welcome back ali87! Menu - Display Messages ('D' or 'd') - Add New Topic ('N' or 'n') - Add Reply to a Topic ('R' or 'r') - Quit ('Q' or 'q') Choose an action: D ------------------------------------------------------------------------------- Message #1: CS12 Assignment 7 from messi: The assignment is hard so go step by step. You can read the Tips & Tricks part for some help. Has anyone started already? Message #6: Re: CS12 Assignment 7 from messi: BUMP Message #9: Re: CS12 Assignment 7 from messi: BUMP ------------------------------------------------------------------------------- Message #2: Your favorite TV show from ricq7: So guys, what is your favorite tv show? Message #3: Re: Your favorite TV show from mike: Game of Thrones is an awesome show but there are too
many characters to remember. We even did not see half of the families and still at least 20 important characters are introduced already. Is there anyplace that I can read about the characters? Message #4: Re: Re: Your favorite TV show from ricq7: Well the book is one hell of a resource. And, the script is written very loyal to the text. Message #8: Re: Re: Re: Your favorite TV show from mike: I'll check that. Message #5: Re: Your favorite TV show from ali87: Whose line is it anyway!!! Too bad, it is no more. Message #7: Re: Re: Your favorite TV show from jenny: The cat! Colin Mochrie is hilarious. ------------------------------------------------------------------------------- Message #10: Count down to Towel Day!! from messi: Bring your towels on May 25. See here: http://www.towelday.org/ ------------------------------------------------------------------------------- Menu - Display Messages ('D' or 'd') - Add New Topic ('N' or 'n') - Add Reply to a Topic ('R' or 'r') - Quit ('Q' or 'q') Choose an action: R Enter Message ID (-1 for Menu): 5 Enter Body: I heard ABC is thinking of reviving the series.
They should definitely hire everyone back.
Menu - Display Messages ('D' or 'd') - Add New Topic ('N' or 'n') - Add Reply to a Topic ('R' or 'r') - Quit ('Q' or 'q') Choose an action: R Enter Message ID (-1 for Menu): 10 Enter Body: I did not know about that. It looks fun.
I'll order this one immediately: http://www.royal-plus.de/dna/indexE.html !!
Menu - Display Messages ('D' or 'd') - Add New Topic ('N' or 'n') - Add Reply to a Topic ('R' or 'r') - Quit ('Q' or 'q') Choose an action: R Enter Message ID (-1 for Menu): 0 Invalid Message ID!! Enter Message ID (-1 for Menu): 15 Invalid Message ID!! Enter Message ID (-1 for Menu): -1 Menu - Display Messages ('D' or 'd') - Add New Topic ('N' or 'n') - Add Reply to a Topic ('R' or 'r') - Quit ('Q' or 'q') Choose an action: D ------------------------------------------------------------------------------- Message #1: CS12 Assignment 7 from messi: The assignment is hard so go step by step. You can read the Tips & Tricks part for some help. Has anyone started already? Message #6: Re: CS12 Assignment 7 from messi: BUMP Message #9: Re: CS12 Assignment 7 from messi: BUMP ------------------------------------------------------------------------------- Message #2: Your favorite TV show from ricq7: So guys, what is your favorite tv show?
Message #3: Re: Your favorite TV show from mike: Game of Thrones is an awesome show but there are too
many characters to remember. We even did not see half of the families and still at least 20 important characters are introduced already. Is there anyplace that I can read about the characters? Message #4: Re: Re: Your favorite TV show from ricq7: Well the book is one hell of a resource. And, the script is written very loyal to the text. Message #8: Re: Re: Re: Your favorite TV show from mike: I'll check that. Message #5: Re: Your favorite TV show from ali87: Whose line is it anyway!!! Too bad, it is no more. Message #7: Re: Re: Your favorite TV show from jenny: The cat! Colin Mochrie is hilarious. Message #11: Re: Re: Your favorite TV show from ali87: I heard that ABC is thinking on reviving the series. They should definitely hire everyone back. ------------------------------------------------------------------------------- Message #10: Count down to Towel Day!! from messi: Bring your towels on May 25. See here: http://www.towelday.org/ Message #12: Re: Count down to Towel Day!! from ali87: I did not know about that. It looks fun. I'll order this one immediately: http://www.royal-plus.de/dna/indexE.html !! ------------------------------------------------------------------------------- Menu - Display Messages ('D' or 'd') - Add New Topic ('N' or 'n') - Add Reply to a Topic ('R' or 'r') - Quit ('Q' or 'q') Choose an action: Q Bye!
Make sure your output matches exactly with the sample runs (given the same inputs) - in particular, note the differences between Topics and Replies.
Step-by-step Approach
Incremental Programming is essential with a project this size.
Copy your work from BBoard (Part 1) assignment.
Message File Read:
Read from a simple file that has only a single Topic, and add the pointer to it to messageList in loadMessages.
Now read from another simple file that has only one Topic and one Reply to that topic, and add it to messageList in loadMessages, and add a pointer to the Reply to the Topic's childList.
Now read in a file with multiple Topics and Replies (you will need a loop, of course!!)
Message File Write:
Build the toFormattedString() function in Reply (Dont forget newline after ). Test it by calling function and outputting the string it returns.
Build the toFormattedString() function in Topic. Test it.
In BBoard::saveMessages() call toFormattedString() on each Message in messageList and print the returning strings to screen/terminal for debugging.
Finally, print the strings to the file instead of the terminal.
Nested Replies and Display:
Modify Message.h, Topic.h and Reply.h files as specified
Modify the print function in Message.cpp to call itself recursively on each of its children after printing the topic itself
In BBoard, modify display and addReply functions as specified.
Have your program run correctly with nested replies
Hints and Tips
Reading from file:
Be very careful assigning "children" (Replies)!!! The children Messages will not be in memory when you are reading the parent, due to their order in the file. One way of doing this would be: For each Message (whether Topic or Reply), maintain a separate string vector that stores the strings following the ":children:" tag (e.g., "5 6 21"). When you finish populating the messageList (or after you close the file), parse the strings (see hint below), construct the Reply objects, and store their POINTERS. e.g.
messageList[i].addChild(messageList[child-1]);
for each element (child) in the corresponding string. Make sure you add an empty string to any string vector with no :children: tag.
Using stringstream to parse message files:
Review the stringstream section! (Section 8.6)
void parseForChildren(vector &childList, const string &filename) { ifstream infile(filename); string discard; int childID; string childIdString; // discard and :children: infile >> discard >> discard; // WARNING: this function is just a HINT - in your actual program you // will need to know when you are dealing with a reply or a topic getline(infile, childIdString); // get string containing all child ids stringstream iss(childIdString); while (iss >> childID) { childList.push_back(childID); } }
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started