#include #include #include #include #include #include #include #include "comment.h" // Function to parse a string field from json line std::string parseStringField(const std::string& json, const std::string& field) { std::string field_name = "\"" + field + "\": \""; size_t pos = json.find(field_name); if (pos == std::string::npos) { return ""; } pos += field_name.length(); size_t end = json.find("\"", pos); return json.substr(pos, end - pos); } // Function to parse a numeric field from json line int parseNumericField(const std::string& json, const std::string& field) { std::string field_name = "\"" + field + "\": "; size_t pos = json.find(field_name); if (pos == std::string::npos) { return 0; } pos += field_name.length(); size_t end = json.find(",", pos); if (end == std::string::npos) { end = json.find("}", pos); } return std::stoi(json.substr(pos, end - pos)); } // Function to parse a boolean field from json line bool parseBooleanField(const std::string& json, const std::string& field) { std::string field_name = "\"" + field + "\": "; size_t pos = json.find(field_name); if (pos == std::string::npos) { return false; } pos += field_name.length(); return json.substr(pos, 4) == "true"; } // Function to recursively display comments with proper indentation void displayComment(Comment* comment, std::ofstream& output, int indent_level = 0) { if (comment == nullptr) { return; } // Create indentation string std::string indent(indent_level * 4, ' '); // Display the comment output << indent << comment->author << " " << comment->published_date << std::endl; output << indent << comment->comment << std::endl; output << indent << "👍 " << comment->like_count << std::endl; // Display reply count if there are replies // We use the original reply_count from the JSON, which includes all descendants if (comment->reply_count > 0) { if (comment->reply_count == 1) { output << indent << comment->reply_count << " reply" << std::endl; } else { output << indent << comment->reply_count << " replies" << std::endl; } } // Recursively display child comments with increased indentation for (size_t i = 0; i < comment->children.size(); ++i) { displayComment(comment->children[i], output, indent_level + 1); } } // Function to recursively delete a comment and all its descendants void deleteCommentRecursive(Comment* comment, std::map& comment_map) { if (comment == nullptr) { return; } // First recursively delete all children for (size_t i = 0; i < comment->children.size(); ++i) { deleteCommentRecursive(comment->children[i], comment_map); } // Remove this comment from the map comment_map.erase(comment->comment_id); } // Function to read and parse the JSON file void parseJsonFile(const std::string& jsonFile, std::map& comment_map, std::vector& root_comments) { // Open the json file std::ifstream json_stream(jsonFile); if (!json_stream.is_open()) { std::cerr << "Failed to open the JSON file." << std::endl; exit(1); } // Read the entire json file into a string std::string json_content; std::string line; while (std::getline(json_stream, line)) { json_content += line; } json_stream.close(); // Find all comments in the json content size_t pos = 0; while ((pos = json_content.find("{\"video_id\":", pos)) != std::string::npos) { // Find the end of this comment size_t end = json_content.find("}", pos); if (end == std::string::npos) break; end++; // Include the closing brace // Extract this comment's json string std::string comment_json = json_content.substr(pos, end - pos); // Parse the fields from the json std::string video_id = parseStringField(comment_json, "video_id"); std::string author = parseStringField(comment_json, "author"); std::string comment_id = parseStringField(comment_json, "comment_id"); int like_count = parseNumericField(comment_json, "like_count"); int reply_count = parseNumericField(comment_json, "reply_count"); bool is_reply = parseBooleanField(comment_json, "is_reply"); std::string parent_comment_id = parseStringField(comment_json, "parent_comment_id"); std::string published_date = parseStringField(comment_json, "published_date"); std::string crawled_date = parseStringField(comment_json, "crawled_date"); bool is_video_owner = parseBooleanField(comment_json, "is_video_owner"); std::string comment_text = parseStringField(comment_json, "comment"); // Create a new Comment object Comment* comment = new Comment(video_id, author, comment_id, like_count, reply_count, is_reply, parent_comment_id, published_date, crawled_date, is_video_owner, comment_text); // Add to the map comment_map[comment_id] = comment; // Move to the next comment pos = end; } } // Function to build the comment tree void buildCommentTree(std::map& comment_map, std::vector& root_comments) { std::map::iterator it; for (it = comment_map.begin(); it != comment_map.end(); ++it) { Comment* comment = it->second; if (comment->is_reply) { // This comment is a reply to another comment std::map::iterator parent_it = comment_map.find(comment->parent_comment_id); if (parent_it != comment_map.end()) { parent_it->second->addChild(comment); } } else { // This comment is a direct response to the video root_comments.push_back(comment); } } } // Function to process a reply to video operation void processReplyToVideo(const std::string& line, std::map& comment_map, std::vector& root_comments) { std::istringstream iss(line); std::string command, comment_id, author, comment_text; iss >> command >> comment_id >> author; // Skip whitespace to get to the comment text iss >> std::ws; // Read the comment text (enclosed in double quotes) if (iss.peek() == '"') { iss.get(); // Consume the opening double quote std::getline(iss, comment_text, '"'); // Read until the closing double quote } // Create a new comment Comment* new_comment = new Comment("", author, comment_id, 0, 0, false, "", "0 seconds ago", "", false, comment_text); // Add to the map and root comments comment_map[comment_id] = new_comment; root_comments.push_back(new_comment); } // Function to process a reply to comment operation void processReplyToComment(const std::string& line, std::map& comment_map) { std::istringstream iss(line); std::string command, parent_id, comment_id, author, comment_text; iss >> command >> parent_id >> comment_id >> author; // Skip whitespace to get to the comment text iss >> std::ws; // Read the comment text (enclosed in double quotes) if (iss.peek() == '"') { iss.get(); // Consume the opening double quote std::getline(iss, comment_text, '"'); // Read until the closing double quote } // Create a new comment Comment* new_comment = new Comment("", author, comment_id, 0, 0, true, parent_id, "0 seconds ago", "", false, comment_text); // Add to the map comment_map[comment_id] = new_comment; // Add as a child to the parent comment std::map::iterator parent_it = comment_map.find(parent_id); if (parent_it != comment_map.end()) { parent_it->second->addChild(new_comment); // Increment the parent's reply_count parent_it->second->reply_count++; } } // Function to process a like comment operation void processLikeComment(const std::string& line, std::map& comment_map) { std::istringstream iss(line); std::string command, comment_id; iss >> command >> comment_id; // Like the comment std::map::iterator it = comment_map.find(comment_id); if (it != comment_map.end()) { it->second->like(); } } // Function to process a delete comment operation void processDeleteComment(const std::string& line, std::map& comment_map, std::vector& root_comments) { std::istringstream iss(line); std::string command, comment_id; iss >> command >> comment_id; // Find the comment to delete std::map::iterator it = comment_map.find(comment_id); if (it != comment_map.end()) { Comment* comment_to_delete = it->second; // If this is a root comment, remove it from root_comments std::vector::iterator root_it = std::find(root_comments.begin(), root_comments.end(), comment_to_delete); if (root_it != root_comments.end()) { root_comments.erase(root_it); } else { // If not a root comment, remove it from its parent's children std::map::iterator parent_it = comment_map.find(comment_to_delete->parent_comment_id); if (parent_it != comment_map.end()) { std::vector& siblings = parent_it->second->children; std::vector::iterator sibling_it = std::find(siblings.begin(), siblings.end(), comment_to_delete); if (sibling_it != siblings.end()) { siblings.erase(sibling_it); // Decrement the parent's reply count parent_it->second->reply_count--; } } } // Recursively delete this comment and all its descendants deleteCommentRecursive(comment_to_delete, comment_map); delete comment_to_delete; } } // Function to process a display comment operation void processDisplayComment(const std::string& line, std::map& comment_map, std::ofstream& output_file) { std::istringstream iss(line); std::string command, comment_id; iss >> command >> comment_id; // Display the comment and its descendants std::map::iterator it = comment_map.find(comment_id); if (it != comment_map.end()) { displayComment(it->second, output_file); } } // Function to process all operations from the operations file void processOperations(const std::string& operationsFile, const std::string& outputFile, std::map& comment_map, std::vector& root_comments) { // Open the operations file std::ifstream ops_file(operationsFile); if (!ops_file.is_open()) { std::cerr << "Failed to open the operations file." << std::endl; exit(1); } // Open the output file std::ofstream output_file(outputFile); if (!output_file.is_open()) { std::cerr << "Failed to open the output file." << std::endl; exit(1); } // Process each operation in the operations file std::string line; while (std::getline(ops_file, line)) { std::istringstream iss(line); std::string command; iss >> command; if (command == "reply_to_video") { processReplyToVideo(line, comment_map, root_comments); } else if (command == "reply_to_comment") { processReplyToComment(line, comment_map); } else if (command == "like_comment") { processLikeComment(line, comment_map); } else if (command == "delete_comment") { processDeleteComment(line, comment_map, root_comments); } else if (command == "display_comment") { processDisplayComment(line, comment_map, output_file); } } // Close files ops_file.close(); output_file.close(); } // Function to clean up memory void cleanupMemory(std::vector& root_comments) { for (size_t i = 0; i < root_comments.size(); ++i) { delete root_comments[i]; } } int main(int argc, char* argv[]) { // Check if the correct number of command line arguments is provided if (argc != 4) { std::cerr << "Usage: " << argv[0] << " input1.json input2.txt output.txt" << std::endl; return 1; } // Parse command line arguments std::string jsonFile = argv[1]; std::string operationsFile = argv[2]; std::string outputFile = argv[3]; // Data structures to store comments std::map comment_map; // Map comment_id to Comment std::vector root_comments; // Comments that are direct responses to the video // Parse the JSON file parseJsonFile(jsonFile, comment_map, root_comments); // Build the comment tree buildCommentTree(comment_map, root_comments); // Process operations processOperations(operationsFile, outputFile, comment_map, root_comments); // Clean up memory cleanupMemory(root_comments); return 0; }