Package net.sf.robocode.host.events

Source Code of net.sf.robocode.host.events.EventManager$DummyCustomEvent

/**
* Copyright (c) 2001-2014 Mathew A. Nelson and Robocode contributors
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://robocode.sourceforge.net/license/epl-v10.html
*/
package net.sf.robocode.host.events;


import net.sf.robocode.host.proxies.BasicRobotProxy;
import net.sf.robocode.security.HiddenAccess;
import robocode.*;
import robocode.exception.EventInterruptedException;
import robocode.robotinterfaces.IBasicRobot;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;


// XXX Remember to update the .NET version whenever a change is made to this class!

/**
* This class is used for managing the event queue for a robot.
*
* @author Mathew A. Nelson (original)
* @author Flemming N. Larsen (contributor)
* @author Matthew Reeder (contributor)
* @author Robert D. Maupin (contributor)
* @author Nathaniel Troutman (contributor)
* @author Pavel Savara (contributor)
*/
public final class EventManager implements IEventManager {

  private final static int MAX_PRIORITY = 100;
  public final static int MAX_EVENT_STACK = 2;
  public final static int MAX_QUEUE_SIZE = 256;

  private final List<Condition> customEvents = new CopyOnWriteArrayList<Condition>();
  private final EventQueue eventQueue;

  private final boolean[] interruptible = new boolean[MAX_PRIORITY + 1];
  private Event currentTopEvent;
  private int currentTopEventPriority;
  private ScannedRobotEvent dummyScannedRobotEvent;
  private Map<String, Event> eventNames;

  private IBasicRobot robot;
  private BasicRobotProxy robotProxy;

  /**
   * Constructs a new EventManager.
   *
   * @param robotProxy the robot proxy that this event manager applies to.
   */
  public EventManager(BasicRobotProxy robotProxy) {
    this.robotProxy = robotProxy;
    eventQueue = new EventQueue();

    registerEventNames();
    reset();
  }

  /**
   * Adds an event to the event queue.
   * @param event is the event to add to the event queue.
   */
  public void add(Event event) {
    if (!HiddenAccess.isCriticalEvent(event)) {
      final int priority = getEventPriority(event.getClass().getName());
      HiddenAccess.setEventPriority(event, priority);
    }
    addImpl(event);
  }

  /**
   * Internal method for adding an event to the event queue.
   * @param event is the event to add to the event queue.
   */
  private void addImpl(Event event) {
    if (eventQueue != null) {
      if (eventQueue.size() > MAX_QUEUE_SIZE) {
        robotProxy.println(
            "Not adding to " + robotProxy.getStatics().getName() + "'s queue, exceeded " + MAX_QUEUE_SIZE
            + " events in queue.");
      } else {
        HiddenAccess.setEventTime(event, getTime());
        eventQueue.add(event);
      }
    }
  }

  /**
   * Adds an custom event to the event queue based on a condition.
   * @param condition is the condition that must be met in order to trigger the custom event.
   */
  public void addCustomEvent(Condition condition) {
    customEvents.add(condition);
  }

  /**
   * Removes all events from the event queue.
   * @param includingSystemEvents {@code true} if system events must be removed as well;
   *                              {@code false} if system events should stay on the event queue.
   */
  public void clearAllEvents(boolean includingSystemEvents) {
    eventQueue.clear(includingSystemEvents);
    // customEvents.clear(); // Custom event should not be cleared here
  }

  /**
   * Cleans up the event queue.
   * <p>
   * This method should be called when the event queue is no longer needed,
   * i.e. before it must be garbage collected.
   */
  public void cleanup() {
    // Remove all events
    reset();

    // Remove all references to robots
    robot = null;
    robotProxy = null;
  }

  /**
   * Returns a list containing all events currently in the robot's queue.
   */
  public List<Event> getAllEvents() {
    List<Event> events = new ArrayList<Event>();
    synchronized (eventQueue) {
      for (Event e : eventQueue) {
        events.add(e);
      }
    }
    return events;
  }

  /**
   * Returns a list containing all BulletHitBulletEvents currently in the robot's queue.
   */
  public List<BulletHitBulletEvent> getBulletHitBulletEvents() {
    List<BulletHitBulletEvent> events = new ArrayList<BulletHitBulletEvent>();
    synchronized (eventQueue) {
      for (Event e : eventQueue) {
        if (e instanceof BulletHitBulletEvent) {
          events.add((BulletHitBulletEvent) e);
        }
      }
    }
    return events;
  }

  /**
   * Returns a list containing all BulletHitEvents currently in the robot's queue.
   */
  public List<BulletHitEvent> getBulletHitEvents() {
    List<BulletHitEvent> events = new ArrayList<BulletHitEvent>();
    synchronized (eventQueue) {
      for (Event e : eventQueue) {
        if (e instanceof BulletHitEvent) {
          events.add((BulletHitEvent) e);
        }
      }
    }
    return events;
  }

  /**
   * Returns a list containing all BulletMissedEvents currently in the robot's queue.
   */
  public List<BulletMissedEvent> getBulletMissedEvents() {
    List<BulletMissedEvent> events = new ArrayList<BulletMissedEvent>();
    synchronized (eventQueue) {
      for (Event e : eventQueue) {
        if (e instanceof BulletMissedEvent) {
          events.add((BulletMissedEvent) e);
        }
      }
    }
    return events;
  }

  /**
   * Returns a list containing all HitByBulletEvents currently in the robot's queue.
   */
  public List<HitByBulletEvent> getHitByBulletEvents() {
    List<HitByBulletEvent> events = new ArrayList<HitByBulletEvent>();
    synchronized (eventQueue) {
      for (Event e : eventQueue) {
        if (e instanceof HitByBulletEvent) {
          events.add((HitByBulletEvent) e);
        }
      }
    }
    return events;
  }

  /**
   * Returns a list containing all HitRobotEvents currently in the robot's queue.
   */
  public List<HitRobotEvent> getHitRobotEvents() {
    List<HitRobotEvent> events = new ArrayList<HitRobotEvent>();

    synchronized (eventQueue) {
      for (Event e : eventQueue) {
        if (e instanceof HitRobotEvent) {
          events.add((HitRobotEvent) e);
        }
      }
    }
    return events;
  }

  /**
   * Returns a list containing all HitWallEvents currently in the robot's queue.
   */
  public List<HitWallEvent> getHitWallEvents() {
    List<HitWallEvent> events = new ArrayList<HitWallEvent>();
    synchronized (eventQueue) {
      for (Event e : eventQueue) {
        if (e instanceof HitWallEvent) {
          events.add((HitWallEvent) e);
        }
      }
    }
    return events;
  }

  /**
   * Returns a list containing all RobotDeathEvents currently in the robot's queue.
   */
  public List<RobotDeathEvent> getRobotDeathEvents() {
    List<RobotDeathEvent> events = new ArrayList<RobotDeathEvent>();
    synchronized (eventQueue) {
      for (Event e : eventQueue) {
        if (e instanceof RobotDeathEvent) {
          events.add((RobotDeathEvent) e);
        }
      }
    }
    return events;
  }

  /**
   * Returns a list containing all ScannedRobotEvents currently in the robot's queue.
   */
  public List<ScannedRobotEvent> getScannedRobotEvents() {
    List<ScannedRobotEvent> events = new ArrayList<ScannedRobotEvent>();
    synchronized (eventQueue) {
      for (Event e : eventQueue) {
        if (e instanceof ScannedRobotEvent) {
          events.add((ScannedRobotEvent) e);
        }
      }
    }
    return events;
  }

  /**
   * Returns a list containing all MessageEvents currently in the robot's queue.
   */
  public List<MessageEvent> getMessageEvents() {
    List<MessageEvent> events = new ArrayList<MessageEvent>();
    synchronized (eventQueue) {
      for (Event e : eventQueue) {
        if (e instanceof MessageEvent) {
          events.add((MessageEvent) e);
        }
      }
    }
    return events;
  }

  /**
   * Returns a list containing all StatusEvents currently in the robot's queue.
   */
  public List<StatusEvent> getStatusEvents() {
    List<StatusEvent> events = new ArrayList<StatusEvent>();
    synchronized (eventQueue) {
      for (Event e : eventQueue) {
        if (e instanceof StatusEvent) {
          events.add((StatusEvent) e);
        }
      }
    }
    return events;
  }

  /**
   * Returns the priority of the current top event.
   */
  public int getCurrentTopEventPriority() {
    return currentTopEventPriority;
  }

  /**
   * Returns the current top event.
   */
  public Event getCurrentTopEvent() {
    return currentTopEvent;
  }

  /**
   * Checks if events with a specific event priority are interruptible.
   * @param priority is the event priority that must be checked.
   * @see #setInterruptible(int, boolean)
   */
  public boolean isInterruptible(int priority) {
    return interruptible[priority];
  }

  /**
   * Sets the robot that will receive events dispatched from the event queue.
   * @param robot is the robot that will receive event dispatched from the event queue.
   */
  public void setRobot(IBasicRobot robot) {
    this.robot = robot;
  }

  /**
   * Returns the priority of a ScannedRobotEvent.
   */
  public int getScannedRobotEventPriority() {
    return dummyScannedRobotEvent.getPriority();
  }

  /**
   * Returns the current time/turn of the battle round.
   */
  public long getTime() {
    return robotProxy.getTimeImpl();
  }

  /**
   * This is the heart of the event manager, which processes the events for a robot.
   */
  public void processEvents() {
    // Remove old events
    eventQueue.clear(getTime() - MAX_EVENT_STACK);

    // Process custom events
    for (Condition customEvent : customEvents) {
      boolean conditionSatisfied = callUserCode(customEvent);
      if (conditionSatisfied) {
        addImpl(new CustomEvent(customEvent));
      }
    }

    // Sort the events based on the time and priority of the events
    eventQueue.sort();

    // Process event queue here
    Event currentEvent;
    while ((currentEvent = (eventQueue.size() > 0) ? eventQueue.get(0) : null) != null
        && currentEvent.getPriority() >= currentTopEventPriority) {

      if (currentEvent.getPriority() == currentTopEventPriority) {
        if (currentTopEventPriority > Integer.MIN_VALUE && isInterruptible(currentTopEventPriority)) {
          setInterruptible(currentTopEventPriority, false); // we're going to restart it, so reset.

          // We are already in an event handler, took action, and a new event was generated.
          // So we want to break out of the old handler to process it here.
          throw new EventInterruptedException(currentEvent.getPriority());
        }
        break;
      }

      int oldTopEventPriority = currentTopEventPriority;

      currentTopEventPriority = currentEvent.getPriority();
      currentTopEvent = currentEvent;

      eventQueue.remove(currentEvent);
      try {
        dispatch(currentEvent);

        setInterruptible(currentTopEventPriority, false);

      } catch (EventInterruptedException e) {
        currentTopEvent = null;
      } catch (RuntimeException e) {
        currentTopEvent = null;
        throw e;
      } catch (Error e) {
        currentTopEvent = null;
        throw e;
      } finally {
        currentTopEventPriority = oldTopEventPriority;       
      }
    }
  }

  /**
   * Checks if the user's condition for a custom event is satisfied.
   * @param condition is the condition to check.
   * @return {@code true} if the condition is satisfied; {@code false} otherwise.
   */
  private boolean callUserCode(Condition condition) {
    boolean conditionSatisfied;
    robotProxy.setTestingCondition(true);
    try {
      conditionSatisfied = condition.test();
    } finally {
      robotProxy.setTestingCondition(false);
    }
    return conditionSatisfied;
  }

  /**
   * Dispatches an event for a robot.
   * <p>
   * Too old events will not be dispatched and a critical event is always dispatched.
   *
   * @param event the event to dispatch to the robot.
   */
  private void dispatch(Event event) {
    if (robot != null && event != null) {
      try {
        // skip too old events
        if ((event.getTime() > getTime() - MAX_EVENT_STACK) || HiddenAccess.isCriticalEvent(event)) {
          HiddenAccess.dispatch(event, robot, robotProxy.getStatics(), robotProxy.getGraphicsImpl());
        }
      } catch (Exception ex) {
        robotProxy.println("SYSTEM: " + ex.getClass().getName() + " occurred on " + event.getClass().getName());
        ex.printStackTrace(robotProxy.getOut());
      }
    }
  }

  /**
   * Removes the custom event with the specified condition from the event queue.
   * @param condition is the condition of the custom event to remove.
   */
  public void removeCustomEvent(Condition condition) {
    customEvents.remove(condition);
  }

  /**
   * Removes all custom events from the event queue.
   */
  public void resetCustomEvents() {
    customEvents.clear();
  }

  /**
   * Resets this event manager by removing all events from the event queue.
   */
  public synchronized void reset() {
    currentTopEventPriority = Integer.MIN_VALUE;
    clearAllEvents(true);
    customEvents.clear();
  }

  /**
   * Changes the interruptible flag for events with a specific priority.
   * When an event is interrupted, events with the same priority are allowed to restart the event handler.
   *
   * @param priority is the priority of the event to set the interruptible flag for.
   * @param isInterruptable {@code true} if events with the specified priority must be interruptible
   *                        allowing events with the same priority to restart the event handler.
   *                        {@code false} if events with the specified priority must not be interruptible
   *                        disallowing events with the same priority to restart the event handler.
   */
  public void setInterruptible(int priority, boolean isInterruptable) {
    if (priority >= 0 && priority < MAX_PRIORITY) {
      interruptible[priority] = isInterruptable;
    }
  }

  /**
   * Returns the priority of events belonging to a specific class.
   * @param eventClass is a string with the full class name of the event type to get the priority from.
   * @return the event priority of the specified event class.
   * @see robocode.Event#getPriority()
   */
  public int getEventPriority(String eventClass) {
    if (eventClass == null) {
      return -1;
    }
    final Event event = eventNames.get(eventClass);
    if (event == null) {
      return -1;
    }
    return event.getPriority();
  }

  /**
   * Sets the event priority of events belonging to a specific class.
   * @param eventClass is a string with the full class name of the event type to set the priority for.
   * @param priority is the new priority
   */
  public void setEventPriority(String eventClass, int priority) {
    if (eventClass == null) {
      return;
    }
    final Event event = eventNames.get(eventClass);
    if (event == null) {
      robotProxy.println("SYSTEM: Unknown event class: " + eventClass);
      return;
    }
    if (HiddenAccess.isCriticalEvent(event)) {
      robotProxy.println("SYSTEM: You may not change the priority of a system event.");
    }
    HiddenAccess.setEventPriority(event, priority);
  }

  /**
   * Registers the full and simple class names of all events used by {@link #getEventPriority(String)} and
   * {@link #setEventPriority(String, int)} and sets the default priority of each event class.
   */
  private void registerEventNames() {
    eventNames = new HashMap<String, Event>();
    dummyScannedRobotEvent = new ScannedRobotEvent(null, 0, 0, 0, 0, 0, false);
    registerEventNames(new BattleEndedEvent(false, null));
    registerEventNames(new BulletHitBulletEvent(null, null));
    registerEventNames(new BulletHitEvent(null, 0, null));
    registerEventNames(new BulletMissedEvent(null));
    registerEventNames(new DeathEvent());
    registerEventNames(new HitByBulletEvent(0, null));
    registerEventNames(new HitRobotEvent(null, 0, 0, false));
    registerEventNames(new HitWallEvent(0));
    registerEventNames(new KeyPressedEvent(null));
    registerEventNames(new KeyReleasedEvent(null));
    registerEventNames(new KeyTypedEvent(null));
    registerEventNames(new MessageEvent(null, null));
    registerEventNames(new MouseClickedEvent(null));
    registerEventNames(new MouseDraggedEvent(null));
    registerEventNames(new MouseEnteredEvent(null));
    registerEventNames(new MouseExitedEvent(null));
    registerEventNames(new MouseMovedEvent(null));
    registerEventNames(new MousePressedEvent(null));
    registerEventNames(new MouseReleasedEvent(null));
    registerEventNames(new MouseWheelMovedEvent(null));
    registerEventNames(new PaintEvent());
    registerEventNames(new RobotDeathEvent(null));
    registerEventNames(new RoundEndedEvent(0, 0, 0));
    registerEventNames(dummyScannedRobotEvent);
    registerEventNames(new SkippedTurnEvent(0));
    registerEventNames(new StatusEvent(null));
    registerEventNames(new WinEvent());

    // same as any line above but for custom event
    final DummyCustomEvent customEvent = new DummyCustomEvent();
    eventNames.put("robocode.CustomEvent", customEvent); // full name with package name
    eventNames.put("CustomEvent", customEvent); // only the class name
  }

  /**
   * Registers the full and simple class name of the specified event and sets the default
   * priority of the event class.
   * @param event an event belonging to the event class to register the class name for etc.
   */
  private void registerEventNames(Event event) {
    if (!HiddenAccess.isCriticalEvent(event)) {
      HiddenAccess.setDefaultPriority(event);
    }
    final Class<?> type = event.getClass();
    eventNames.put(type.getName(), event); // full name with package name
    eventNames.put(type.getSimpleName(), event); // only the class name
  }

  /**
   * A dummy CustomEvent used only for registering the class name.
   */
  @SuppressWarnings("serial")
  private static final class DummyCustomEvent extends CustomEvent {
    public DummyCustomEvent() {
      super(null);
    }
  }
}
TOP

Related Classes of net.sf.robocode.host.events.EventManager$DummyCustomEvent

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.