All Downloads are FREE. Search and download functionalities are using the official Maven repository.

rinde.sim.scenario.ScenarioBuilder Maven / Gradle / Ivy

There is a newer version: 4.4.6
Show newest version
package rinde.sim.scenario;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.newLinkedList;

import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;

/**
 * The ScenarioBuilder is a helper class to easily generate {@link Scenario}
 * instances. It provides a set of easy to use methods and it is extensible by
 * adding {@link EventGenerator}s to the builder. The specific class of
 * {@link TimedEvent} that should be generated can be changed by using
 * {@link EventCreator}.
 * 
 * @author Bartosz Michalik 
 * @author Rinde van Lon 
 */
public class ScenarioBuilder {

  final Set> supportedTypes;
  private final List> generators;
  private final List events;

  /**
   * Initializes a new ScenarioBuilder which supports the specified types of
   * events.
   * @param pSupportedTypes The event types this ScenarioBuilder supports.
   */
  public ScenarioBuilder(Set> pSupportedTypes) {
    this(ImmutableSet.copyOf(pSupportedTypes));
  }

  /**
   * Initializes a new ScenarioBuilder which supports the specified types of
   * events.
   * @param pSupportedTypes The event types this ScenarioBuilder supports.
   */
  public ScenarioBuilder(Enum... pSupportedTypes) {
    this(ImmutableSet.copyOf(pSupportedTypes));
  }

  /**
   * Initializes a new ScenarioBuilder which supports the specified types of
   * events.
   * @param types The event types this ScenarioBuilder supports.
   */
  protected ScenarioBuilder(ImmutableSet> types) {
    supportedTypes = types;
    generators = newLinkedList();
    events = newArrayList();
  }

  /**
   * Add an {@link EventGenerator} to this ScenarioBuilder.
   * @param generator The generator to add.
   * @return this
   */
  public ScenarioBuilder addEventGenerator(
      EventGenerator generator) {
    checkArgument(generator != null, "generator can not be null");
    generators.add(generator);
    return this;
  }

  /**
   * Convenience method for adding a single event.
   * @param event The {@link TimedEvent} to add.
   * @return this
   */
  public ScenarioBuilder addEvent(TimedEvent event) {
    checkArgument(supportedTypes.contains(event.getEventType()), "%s is not a supported event type of this ScenarioBuilder, it should be added in its constructor.", event
        .getEventType());
    events.add(event);
    return this;
  }

  /**
   * Convenience method for adding events.
   * @param es The {@link TimedEvent} to add.
   * @return this
   */
  public ScenarioBuilder addEvents(TimedEvent... es) {
    for (final TimedEvent te : es) {
      addEvent(te);
    }
    return this;
  }

  /**
   * Convenience method for adding events.
   * @param es The {@link TimedEvent} to add.
   * @return this
   */
  public ScenarioBuilder addEvents(Collection es) {
    for (final TimedEvent te : es) {
      addEvent(te);
    }
    return this;
  }

  /**
   * Adds multiple events using a creator.
   * @param time The time at which the {@link TimedEvent} will be added.
   * @param amount The amount of events to add.
   * @param eventCreator The {@link EventCreator} that instantiates the events.
   * @return this
   */
  public  ScenarioBuilder addMultipleEvents(long time,
      int amount, EventCreator eventCreator) {
    generators.add(new MultipleEventGenerator(time, amount, eventCreator));
    return this;
  }

  /**
   * Adds multiple {@link TimedEvent} using the specified type.
   * @param time The time at which the {@link TimedEvent} will be added.
   * @param amount The amount of events to add.
   * @param type The type of event to add.
   * @return this
   */
  public ScenarioBuilder addMultipleEvents(long time, int amount, Enum type) {
    addMultipleEvents(time, amount, new EventTypeFunction(type));
    return this;
  }

  /**
   * Adds a series of events. The first event will be added at
   * startTime, the next events will be added with intervals of
   * size timeStep up to and including
   * endTime
   * @param startTime The time of the first event in the series.
   * @param endTime The end time of the series.
   * @param timeStep The interval between events.
   * @param eventCreator The {@link EventCreator} that creates events.
   * @return this
   */
  public  ScenarioBuilder addTimeSeriesOfEvents(
      long startTime, long endTime, long timeStep, EventCreator eventCreator) {
    generators
        .add(new TimeSeries(startTime, endTime, timeStep, eventCreator));
    return this;
  }

  /**
   * Adds a series of events. The first event will be added at
   * startTime, the next events will be added with intervals of
   * size timeStep up to and including
   * endTime
   * @param startTime The time of the first event in the series.
   * @param endTime The end time of the series.
   * @param timeStep The interval between events.
   * @param type The type of event to add.
   * @return this
   */
  public  ScenarioBuilder addTimeSeriesOfEvents(
      long startTime, long endTime, long timeStep, Enum type) {
    addTimeSeriesOfEvents(startTime, endTime, timeStep, new EventTypeFunction(
        type));
    return this;
  }

  /**
   * @return the current events as a sorted list.
   */
  protected List buildEventList() {
    final List es = newArrayList(events);
    for (final EventGenerator g : generators) {
      final Collection collection = g.generate();
      for (final TimedEvent te : collection) {
        checkArgument(supportedTypes.contains(te.getEventType()), "%s is not supported by this ScenarioBuilder", te
            .getEventType());
        es.add(te);
      }
    }
    Collections.sort(es, new TimeComparator());
    return es;
  }

  /**
   * Generates a new {@link Scenario}.
   * @return The new scenario.
   */
  public Scenario build() {
    return build(new ScenarioCreator() {
      @Override
      public Scenario create(List eventList, Set> eventTypes) {
        return new Scenario(eventList, supportedTypes);
      }
    });
  }

  /**
   * Build the scenario using the specified {@link ScenarioCreator}.
   * @param sc ScenarioCreator which instantiates the scenario.
   * @return A scenario.
   */
  public  T build(ScenarioCreator sc) {
    return sc.create(buildEventList(), supportedTypes);
  }

  /**
   * Checks whether the specified scenario is time consistent, i.e. all events
   * should be sorted by time.
   * @param scen The scenario to check.
   * @return true if it is consistent, false
   *         otherwise.
   */
  public static boolean isTimeOrderingConsistent(Scenario scen) {
    final List es = newArrayList(scen.asList());
    Collections.sort(es, new TimeComparator());
    return scen.asList().equals(es);
  }

  /**
   * A scenario creator can be used to create custom scenarios (subclasses of
   * {@link Scenario}.
   * @param  The type of scenario the creator creates.
   * @author Rinde van Lon 
   */
  public interface ScenarioCreator {
    /**
     * @param eventList
     * @param eventTypes
     * @return The scenario
     */
    T create(List eventList, Set> eventTypes);
  }

  /**
   * Classes that implement this interface can be used to generate a sequence of
   * events.
   * @param  The subtype of {@link TimedEvent} that is generated.
   * 
   * @author Bartosz Michalik 
   * @author Rinde van Lon 
   */
  public interface EventGenerator {
    /**
     * @return A sequence of events.
     */
    Collection generate();
  }

  /**
   * An {@link EventGenerator} that generates multiple events.
   * @param  The subtype of {@link TimedEvent} to generate.
   * 
   * @author Bartosz Michalik 
   * @author Rinde van Lon 
   */
  public static class MultipleEventGenerator implements
      EventGenerator {
    private final long time;
    private final int amount;
    private final Function eventCreator;

    /**
     * Instantiate the MultipleEventGenerator.
     * @param pTime The time at which the {@link TimedEvent}s will be added.
     * @param pAmount The amount of events to add.
     * @param pEventCreator The {@link EventCreator} that instantiates the
     *          events.
     */
    public MultipleEventGenerator(long pTime, int pAmount,
        EventCreator pEventCreator) {
      checkArgument(pTime >= 0, "time can not be negative");
      checkArgument(pAmount >= 1, "amount must be at least 1");
      checkArgument(pEventCreator != null, "event creator can not be null");
      time = pTime;
      amount = pAmount;
      eventCreator = pEventCreator;
    }

    @Override
    public Collection generate() {
      final List result = new LinkedList();
      for (int i = 0; i < amount; ++i) {
        result.add(eventCreator.apply(time));
      }
      return result;
    }
  }

  /**
   * An {@link EventGenerator} that generates events using a fixed interval. The
   * first event will be added at startTime, the next events will
   * be added with intervals of size timeStep up to and
   * including endTime
   * @param  The subtype of {@link TimedEvent} to generate.
   * 
   * @author Bartosz Michalik 
   * @author Rinde van Lon ([email protected])
   */
  public static class TimeSeries implements
      EventGenerator {
    private final long start;
    private final long end;
    private final long step;
    private final Function eventCreator;

    /**
     * Instantiates a new TimeSeries.
     * @param pStartTime The time of the first event in the series.
     * @param pEndTime The end time of the series.
     * @param pTimeStep The interval between events.
     * @param pEventCreator The {@link EventCreator} that creates events.
     */
    public TimeSeries(long pStartTime, long pEndTime, long pTimeStep,
        EventCreator pEventCreator) {
      checkArgument(pStartTime < pEndTime, "start time must be before end time");
      checkArgument(pEndTime > 0, "end time must be greater than 0");
      checkArgument(pTimeStep >= 1, "time step must be >= than 1");
      checkArgument(pEventCreator != null, "event creator can not be null");
      start = pStartTime;
      end = pEndTime;
      step = pTimeStep;
      eventCreator = pEventCreator;
    }

    @Override
    public Collection generate() {
      final List result = new LinkedList();
      for (long t = start; t <= end; t += step) {
        result.add(eventCreator.apply(t));
      }
      return result;
    }
  }

  /**
   * Instances create {@link TimedEvent}s at specified times.
   * @param  The subtype of {@link TimedEvent} to create.
   * @author Rinde van Lon ([email protected])
   */
  public static abstract class EventCreator implements
      Function {}

  /**
   * An {@link EventCreator} that creates {@link TimedEvent}s with the specified
   * type.
   * 
   * @author Bartosz Michalik 
   * @author Rinde van Lon ([email protected])
   */
  public static class EventTypeFunction extends EventCreator {

    private final Enum typeEvent;

    /**
     * @param type The type which events created by this creator will have.
     */
    public EventTypeFunction(Enum type) {
      checkArgument(type != null);
      typeEvent = type;
    }

    @Override
    public TimedEvent apply(Long input) {
      return new TimedEvent(typeEvent, input);
    }

  }

  /**
   * Comparator for comparing {@link TimedEvent}s on their time.
   * @author Rinde van Lon 
   */
  public static class TimeComparator implements Comparator,
      Serializable {
    private static final long serialVersionUID = -2711991793346719648L;

    /**
     * Instantiate.
     */
    public TimeComparator() {}

    @Override
    public int compare(TimedEvent o1, TimedEvent o2) {
      return (int) (o1.time - o2.time);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy