class Tilia::VObject::FreeBusyData

FreeBusyData is a helper class that manages freebusy information.

Attributes

data[R]

Public Class Methods

new(start, ending) click to toggle source

A list of free-busy times.

@return [array] RUBY: attr_accessor :data

# File lib/tilia/v_object/free_busy_data.rb, line 20
def initialize(start, ending)
  @start = start
  @end = ending
  @data = []

  @data << {
    'start' => @start,
    'end'   => @end,
    'type'  => 'FREE'
  }
end

Public Instance Methods

add(start, ending, type) click to toggle source

Adds free or busytime to the data.

@param [Fixnum] start @param [Fixnum] end @param [String] type FREE, BUSY, BUSY-UNAVAILABLE or BUSY-TENTATIVE @return [void]

# File lib/tilia/v_object/free_busy_data.rb, line 38
def add(start, ending, type)
  if start > @end || ending < @start
    # This new data is outside our timerange.
    return nil
  end

  if start < @start
    # The item starts before our requested time range
    start = @start
  end
  if ending > @end
    # The item ends after our requested time range
    ending = @end
  end

  # Finding out where we need to insert the new item.
  current_index = 0
  current_index += 1 while start > @data[current_index]['end']

  # The standard insertion point will be one _after_ the first
  # overlapping item.
  insert_start_index = current_index + 1

  new_item = {
    'start' => start,
    'end'   => ending,
    'type'  => type
  }

  preceeding_item = @data[insert_start_index - 1]
  if @data[insert_start_index - 1]['start'] == start
    # The old item starts at the exact same point as the new item.
    insert_start_index -= 1
  end

  # Now we know where to insert the item, we need to know where it
  # starts overlapping with items on the tail end. We need to start
  # looking one item before the insertStartIndex, because it's possible
  # that the new item 'sits inside' the previous old item.
  if insert_start_index > 0
    current_index = insert_start_index - 1
  else
    current_index = 0
  end

  current_index += 1 while ending > @data[current_index]['end']

  # What we are about to insert into the array
  new_items = [new_item]

  # This is the amount of items that are completely overwritten by the
  # new item.
  items_to_delete = current_index - insert_start_index
  items_to_delete += 1 if @data[current_index]['end'] <= ending

  # If itemsToDelete was -1, it means that the newly inserted item is
  # actually sitting inside an existing one. This means we need to split
  # the item at the current position in two and insert the new item in
  # between.
  if items_to_delete == -1
    items_to_delete = 0
    if new_item['end'] < preceeding_item['end']
      new_items << {
        'start' => new_item['end'] + 1,
        'end'   => preceeding_item['end'],
        'type'  => preceeding_item['type']
      }
    end
  end

  @data[insert_start_index, items_to_delete] = new_items

  do_merge = false
  merge_offset = insert_start_index
  merge_item = new_item
  merge_delete = 1

  # Ruby knows negative indices as well!
  if insert_start_index > 0 && @data.size > insert_start_index - 1
    # Updating the start time of the previous item.
    @data[insert_start_index - 1]['end'] = start

    # If the previous and the current are of the same type, we can
    # merge them into one item.
    if @data[insert_start_index - 1]['type'] == @data[insert_start_index]['type']
      do_merge = true
      merge_offset -= 1
      merge_delete += 1
      merge_item['start'] = @data[insert_start_index - 1]['start']
    end
  end

  if @data.size > insert_start_index + 1
    # Updating the start time of the next item.
    @data[insert_start_index + 1]['start'] = ending

    # If the next and the current are of the same type, we can
    # merge them into one item.
    if @data[insert_start_index + 1]['type'] == @data[insert_start_index]['type']
      do_merge = true
      merge_delete += 1
      merge_item['end'] = @data[insert_start_index + 1]['end']
    end
  end

  @data[merge_offset, merge_delete] = merge_item if do_merge
end