// The reservations data structure will look something like the diagram // below. All times slots are sorted, with the earliest reservations // at the beginning of the linked list. No new reservation will be // allowed if it's time interval overlaps an existing reservation. // // +--------------+ +-----------------+ +-----------------+ // | class | | first | | next | // | reservations | | time slot | | time slot | // |--------------| | (name, | | (name, | // | head |--->| start time, | | start time, | // +--------------+ | etc) | | etc) | // |-----------------| |-----------------| // | next |--->| next |---> NULL // +-----------------+ +-----------------+ #include #include #include #include #include "debug.h" #include "errors.h" #include "time_slot.h" #include "reservations.h" reservations::reservations() { head = NULL; count = 0; } reservations::~reservations() { res_element *tmp; while (head != NULL) { // step 1: point tmp at the first list element // step 2: point head at the next list element // step 3: get rid of tmp tmp = head; head = head->next; #ifdef FBFC_DEBUG cerr << "\nremoving another reservation\n"; #endif delete tmp; } } int reservations::add(const time_slot_info new_slot_info) { res_element *new_res; int outcome = SUCCESSFUL; res_element *insert_after_this; if (new_slot_info.finish < new_slot_info.start) { outcome = RES_ERR_START_FINISH; } else { // load up a new reservation with all the proper info new_res = new res_element; new_res->next = NULL; outcome = new_res->res_data.update(new_slot_info); if (outcome == SUCCESSFUL) { // find where this new time slot should be placed relative to the // existing, time-ordered time slots insert_after_this = find_insert_point(head, new_slot_info.start, new_slot_info.finish, outcome); if (outcome == SUCCESSFUL) { if (insert_after_this == NULL) { // put it at the front of the list #ifdef FBFC_DEBUG cerr << "\nadding " << new_slot_info.name << " to beginning of the list\n"; #endif if (head != NULL) { // the "next" pointer in the new list element will point at // the first record of the old list new_res->next = head; } head = new_res; } else { #ifdef FBFC_DEBUG cerr << "\nadding " << new_slot_info.name << " inside existing list\n"; #endif // The new reservation will go somewhere inside the list. // new_res->next will need to point at the same thing // that insert_after_this->next is currently pointing at new_res->next = insert_after_this->next; // update insert_after_this->next so it now points at the new // list element we just created insert_after_this->next = new_res; } } } } if (outcome == SUCCESSFUL) { ++count; } return outcome; } // This function collects all the individual reservations, called // "time_slots". // The calling function will be responsible for freeing all memory // space for time_slot_info structures time_slot_info *reservations::get_all(int &slot_count) { // allocate enough memory to hold all the time slot records slot_count = count; time_slot_info *slots = new time_slot_info [slot_count]; int i; res_element *slot_ptr; // make a copy of each time slot slot_ptr = head; for (i = 0; i < slot_count; ++i) { slots[i] = slot_ptr->res_data.get(); slot_ptr = slot_ptr->next; } return slots; } // Remove the proper element from the linked list. To do this, we really need // to find the list element just prior to the one being deleted, so we can // reset the "next" field of that prior element to point at the element that // comes after the element we are deleting. int reservations::remove(const int slot_unique_id) { int found = 0; int finished = 0; res_element *prior_ptr = head; res_element *target; int outcome = SUCCESSFUL; time_slot_info list_info; // check each element of the linked list for the proper id if (head == NULL) { outcome = RES_ERR_NOT_FOUND; } else { // if the list element to be deleted is at the very beginning of the // linked list, we need some special logic to make sure "head" gets // updated properly. If the element to be deleted is further down the // list, then that logic can be put inside a generic "while" loop. list_info = head->res_data.get(); if (list_info.slot_unique_id == slot_unique_id) { // get rid of the first linked list element, and point head at the // second element target = head; head = head->next; delete target; --count; } else // set up a loop to look through the rest of the linked list { while ( (!found) && (!finished) ) { if (prior_ptr->next == NULL) // nuts ... we got to the end of the list without finding // what we were looking for { finished = 1; } else { // check the id of the element that follows prior_ptr list_info = prior_ptr->next->res_data.get(); if (list_info.slot_unique_id == slot_unique_id) { found = 1; } else { // move prior_ptr down to the next list element prior_ptr = prior_ptr->next; } } } if (!found) { outcome = RES_ERR_NOT_FOUND; } else { // relink the list to bypass the target, then delete the target target = prior_ptr->next; prior_ptr->next = prior_ptr->next->next; delete target; --count; } } } return outcome; } // Find out where the target time should be inserted in the ordered linked list // of time slots. Return a pointer to the res_element that target should follow. // If target should be placed at the very beginning of the linked list, return // NULL. Calling functions should check the value of "outcome" before doing // any other processing. res_element *find_insert_point(res_element *head, const time_t target_start, const time_t target_finish, int &outcome) { res_element *current_res_ptr = NULL; res_element *next_res_ptr = NULL; time_slot_info current_slot_info; time_slot_info next_slot_info; int found = 0; int target_position; outcome = SUCCESSFUL; // if the linked list is empty, the target should obviously be added // as the first element of the linked list if (head != NULL) { current_res_ptr = head; current_slot_info = current_res_ptr->res_data.get(); target_position = compare_times(target_start, target_finish, current_slot_info.start, current_slot_info.finish); if (target_position == RES_CONFLICT) { outcome = RES_ERR_CONFLICT; } else if (target_position == RES_EARLIER) { // since the target is earlier than the first element of the // linked list, it should be added to the very beginning of // the list current_res_ptr = NULL; } else { while (! found) { // We know that current_slot_info has an earlier start time than // the target parameter. See if the next time slot also has // an earlier start time next_res_ptr = current_res_ptr->next; if (next_res_ptr == NULL) { // there is no next time slot, so the target will be added // after the last element in the linked list found = 1; } else { next_slot_info = next_res_ptr->res_data.get(); target_position = compare_times(target_start, target_finish, next_slot_info.start, next_slot_info.finish); if (target_position == RES_CONFLICT) { outcome = RES_ERR_CONFLICT; found = 1; } else if (target_position == RES_EARLIER) { // the new reservaton should go between the current_slot and // and the next_slot found = 1; } else { // the next linked list element becomes the current linked list // element and we repeat the search logic current_res_ptr = next_res_ptr; current_slot_info = next_slot_info; } } } } } return current_res_ptr; } // Are the new start/finish time earlier, later, or overlapping the // existing start/finish times? int compare_times(const time_t new_start_time, const time_t new_finish_time, const time_t existing_start_time, const time_t existing_finish_time) { int outcome; if ( ( (new_start_time >= existing_start_time) && (new_start_time < existing_finish_time) ) || ( (new_finish_time > existing_start_time) && (new_finish_time <= existing_finish_time) ) ) { outcome = RES_CONFLICT; } else if (new_finish_time <= existing_start_time) { outcome = RES_EARLIER; } else { outcome = RES_LATER; } return outcome; }