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.
+ *
+ * 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 getAllEvents() {
+ List events = new ArrayList();
+ 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 getBulletHitBulletEvents() {
+ List events = new ArrayList();
+ 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 getBulletHitEvents() {
+ List events = new ArrayList();
+ 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 getBulletMissedEvents() {
+ List events = new ArrayList();
+ 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 getHitByBulletEvents() {
+ List events = new ArrayList();
+ 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 getHitRobotEvents() {
+ List events = new ArrayList();
+
+ 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 getHitWallEvents() {
+ List events = new ArrayList();
+ 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 getRobotDeathEvents() {
+ List events = new ArrayList();
+ 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 getScannedRobotEvents() {
+ List events = new ArrayList();
+ 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 getMessageEvents() {
+ List events = new ArrayList();
+ 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 getStatusEvents() {
+ List events = new ArrayList();
+ 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 (MyException 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.
+ *
+ * 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();
+ 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);
+ }
+ }
+}
diff --git a/代码/workspace_robo4/robocode.host/src/main/java/net/sf/robocode/host/proxies/HostingRobotProxy.java b/代码/workspace_robo4/robocode.host/src/main/java/net/sf/robocode/host/proxies/HostingRobotProxy.java
new file mode 100644
index 0000000..9fdb31e
--- /dev/null
+++ b/代码/workspace_robo4/robocode.host/src/main/java/net/sf/robocode/host/proxies/HostingRobotProxy.java
@@ -0,0 +1,306 @@
+/**
+ * Copyright (c) 2001-2016 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.proxies;
+
+
+import net.sf.robocode.host.events.EventManager;
+import net.sf.robocode.host.io.RobotFileSystemManager;
+import net.sf.robocode.host.io.RobotOutputStream;
+import net.sf.robocode.host.security.RobotThreadManager;
+import net.sf.robocode.host.*;
+import static net.sf.robocode.io.Logger.logError;
+import static net.sf.robocode.io.Logger.logMessage;
+import net.sf.robocode.peer.BadBehavior;
+import net.sf.robocode.peer.ExecCommands;
+import net.sf.robocode.peer.IRobotPeer;
+import net.sf.robocode.repository.IRobotItem;
+import net.sf.robocode.core.Container;
+import robocode.RobotStatus;
+import robocode.exception.AbortedException;
+import robocode.exception.DeathException;
+import robocode.exception.DisabledException;
+import robocode.exception.WinException;
+import robocode.robotinterfaces.IBasicRobot;
+import robocode.robotinterfaces.peer.IBasicRobotPeer;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+
+// XXX Remember to update the .NET version whenever a change is made to this class!
+
+/**
+ * @author Pavel Savara (original)
+ */
+abstract class HostingRobotProxy implements IHostingRobotProxy, IHostedThread {
+
+ private final IRobotItem robotSpecification;
+
+ protected EventManager eventManager;
+ private final IHostManager hostManager;
+ protected RobotThreadManager robotThreadManager;
+ protected RobotFileSystemManager robotFileSystemManager;
+ private IThreadManager threadManager;
+
+ private IBasicRobot robot;
+ protected final IRobotPeer peer;
+ protected IRobotClassLoader robotClassLoader;
+
+ protected final RobotStatics statics;
+ protected RobotOutputStream out;
+
+ private final Set securityViolations = Collections.synchronizedSet(new HashSet());
+
+ HostingRobotProxy(IRobotItem robotSpecification, IHostManager hostManager, IRobotPeer peer, RobotStatics statics) {
+ this.peer = peer;
+ this.statics = statics;
+ this.hostManager = hostManager;
+ this.robotSpecification = robotSpecification;
+
+ robotClassLoader = getHost(robotSpecification).createLoader(robotSpecification);
+ robotClassLoader.setRobotProxy(this);
+
+ out = new RobotOutputStream();
+ robotThreadManager = new RobotThreadManager(this);
+
+ loadClassBattle();
+
+ robotFileSystemManager = new RobotFileSystemManager(this, hostManager.getRobotFilesystemQuota(),
+ robotSpecification.getWritableDirectory(), robotSpecification.getReadableDirectory(),
+ robotSpecification.getRootPath());
+
+ robotFileSystemManager.initialize();
+ }
+
+ private JavaHost getHost(IRobotItem robotSpecification) {
+ return (JavaHost) Container.cache.getComponent("robocode.host." + robotSpecification.getPlatform().toLowerCase());
+ }
+
+ public void cleanup() {
+ robot = null;
+
+ // Remove the file system and the manager
+ robotFileSystemManager = null;
+ if (out != null) {
+ out.close();
+ out = null;
+ }
+
+ if (robotThreadManager != null) {
+ robotThreadManager.cleanup();
+ }
+ robotThreadManager = null;
+
+ // Cleanup and remove class manager
+ if (robotClassLoader != null) {
+ robotClassLoader.cleanup();
+ robotClassLoader = null;
+ }
+ }
+
+ public RobotOutputStream getOut() {
+ return out;
+ }
+
+ public void println(String s) {
+ out.println(s);
+ }
+
+ private void println(Throwable ex) {
+ ex.printStackTrace(out);
+ }
+
+ public RobotStatics getStatics() {
+ return statics;
+ }
+
+ public RobotFileSystemManager getRobotFileSystemManager() {
+ return robotFileSystemManager;
+ }
+
+ public ClassLoader getRobotClassloader() {
+ return (ClassLoader) robotClassLoader;
+ }
+
+ // -----------
+ // battle driven methods
+ // -----------
+
+ protected abstract void initializeRound(ExecCommands commands, RobotStatus status);
+
+ public void startRound(ExecCommands commands, RobotStatus status) {
+ initializeRound(commands, status);
+ threadManager = ((HostManager) hostManager).getThreadManager();
+ robotThreadManager.start(threadManager);
+ }
+
+ public void forceStopThread() {
+ if (!robotThreadManager.forceStop()) {
+ peer.punishBadBehavior(BadBehavior.UNSTOPPABLE);
+ peer.setRunning(false);
+ }
+ }
+
+ public void waitForStopThread() {
+ if (!robotThreadManager.waitForStop()) {
+ peer.punishBadBehavior(BadBehavior.UNSTOPPABLE);
+ peer.setRunning(false);
+ }
+ }
+
+ private void loadClassBattle() {
+ try {
+ robotClassLoader.loadRobotMainClass(true);
+ } catch (RuntimeException e) {
+ println("SYSTEM: Could not load " + statics.getName() + " : ");
+ println(e);
+ drainEnergy();
+ }
+ }
+
+ private boolean loadRobotRound() {
+ robot = null;
+ try {
+ threadManager.setLoadingRobot(this);
+ robot = robotClassLoader.createRobotInstance();
+ if (robot == null) {
+ println("SYSTEM: Skipping robot: " + statics.getName());
+ return false;
+ }
+ robot.setOut(out);
+ robot.setPeer((IBasicRobotPeer) this);
+ eventManager.setRobot(robot);
+ } catch (IllegalAccessException e) {
+ println("SYSTEM: Unable to instantiate this robot: " + e);
+ println("SYSTEM: Is your constructor marked public?");
+ println(e);
+ robot = null;
+ logError(e);
+ return false;
+ } catch (RuntimeException e) {
+ println("SYSTEM: An error occurred during initialization of " + statics.getName());
+ println("SYSTEM: " + e);
+ println(e);
+ robot = null;
+ logError(e);
+ return false;
+ } finally {
+ threadManager.setLoadingRobot(null);
+ }
+ return true;
+ }
+
+ protected abstract void executeImpl();
+
+ public void run() {
+ // Only initialize AWT if we are not running in headless mode.
+ // Bugfix [2833271] IllegalThreadStateException with the AWT-Shutdown thread.
+ // Read more about headless mode here:
+ // http://java.sun.com/developer/technicalArticles/J2SE/Desktop/headless/
+ if (System.getProperty("java.awt.headless", "true").equals("false")) {
+ robotThreadManager.initAWT();
+ }
+
+ if (robotSpecification.isValid() && loadRobotRound()) {
+ try {
+ if (robot != null) {
+ peer.setRunning(true);
+
+ // Process all events for the first turn.
+ // This is done as the first robot status event must occur before the robot
+ // has started running.
+ eventManager.processEvents();
+
+ // Call user code
+ callUserCode();
+ }
+ while (peer.isRunning()) {
+ executeImpl();
+ }
+ } catch (WinException e) {// Do nothing
+ } catch (AbortedException e) {// Do nothing
+ } catch (DeathException e) {
+ println("SYSTEM: " + statics.getName() + " has died");
+ } catch (DisabledException e) {
+ drainEnergy();
+
+ String msg = e.getMessage();
+ if (msg == null) {
+ msg = "";
+ } else {
+ msg = ": " + msg;
+ }
+ println("SYSTEM: Robot disabled: " + msg);
+ logMessage("Robot disabled: " + statics.getName());
+ } catch (Exception e) {
+ drainEnergy();
+ println(e);
+ logMessage(statics.getName() + ": Exception: " + e); // without stack here
+ } catch (ThreadDeath e) {
+ drainEnergy();
+ println(e);
+ logMessage(statics.getName() + " stopped successfully.");
+ throw e; // must be re-thrown in order to stop the thread
+ } catch (RuntimeException t) {
+ drainEnergy();
+ println(t);
+ logMessage(statics.getName() + ": Throwable: " + t); // without stack here
+ } finally {
+ waitForBattleEndImpl();
+ }
+ } else {
+ drainEnergy();
+ peer.punishBadBehavior(BadBehavior.CANNOT_START);
+ waitForBattleEndImpl();
+ }
+
+ peer.setRunning(false);
+
+ // If battle is waiting for us, well, all done!
+ synchronized (this) {
+ notifyAll();
+ }
+ }
+
+ private void callUserCode() {
+ Runnable runnable = robot.getRobotRunnable();
+ if (runnable != null) {
+ runnable.run();
+ }
+ }
+
+ protected abstract void waitForBattleEndImpl();
+
+ public void drainEnergy() {
+ peer.drainEnergy();
+ }
+
+ public void punishSecurityViolation(String message) {
+ // Prevent unit tests of failing if multiple threads are calling this method in the same time.
+ // We only want the a specific type of security violation logged once so we only get one error
+ // per security violation.
+ synchronized (securityViolations) {
+ String key = message;
+
+ if (key.startsWith("Preventing Thread-")) {
+ key = key.replaceAll("\\d+", "X");
+ }
+ if (!securityViolations.contains(key)) {
+ securityViolations.add(key);
+ logError(message);
+ println("SYSTEM: " + message);
+
+ if (securityViolations.size() == 1) {
+ peer.drainEnergy();
+ peer.punishBadBehavior(BadBehavior.SECURITY_VIOLATION);
+ }
+ }
+ }
+ }
+}
diff --git a/代码/workspace_robo4/robocode.host/src/main/java/net/sf/robocode/host/security/RobotClassLoader.java b/代码/workspace_robo4/robocode.host/src/main/java/net/sf/robocode/host/security/RobotClassLoader.java
new file mode 100644
index 0000000..0993839
--- /dev/null
+++ b/代码/workspace_robo4/robocode.host/src/main/java/net/sf/robocode/host/security/RobotClassLoader.java
@@ -0,0 +1,424 @@
+/**
+ * Copyright (c) 2001-2016 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.security;
+
+
+import net.sf.robocode.core.Container;
+import net.sf.robocode.host.IHostedThread;
+import net.sf.robocode.host.IRobotClassLoader;
+import net.sf.robocode.io.FileUtil;
+import net.sf.robocode.io.Logger;
+import net.sf.robocode.io.RobocodeProperties;
+import net.sf.robocode.io.URLJarCollector;
+import robocode.robotinterfaces.IBasicRobot;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.net.URLConnection;
+import java.nio.ByteBuffer;
+import java.security.*;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+
+/**
+ * This class loader is used by robots. It isolates classes which belong to robot and load them locally.
+ * General java classes or robocode.api classes are loaded by parent loader and shared with Robocode engine.
+ * Attempts to load classes of Robocode engine are blocked.
+ *
+ * @author Mathew A. Nelson (original)
+ * @author Flemming N. Larsen (contributor)
+ * @author Matthew Reeder (contributor)
+ * @author Robert D. Maupin (contributor)
+ * @author Nathaniel Troutman (contributor)
+ */
+public class RobotClassLoader extends URLClassLoader implements IRobotClassLoader {
+
+ static final String UNTRUSTED_URL = "http://robocode.sf.net/untrusted";
+
+ private static final PermissionCollection EMPTY_PERMISSIONS = new Permissions();
+
+ private final String fullClassName;
+
+ private ClassLoader parent;
+ private CodeSource codeSource;
+
+ private IHostedThread robotProxy;
+ private Class> robotClass;
+
+ // Names on classes referenced from the robot class
+ private Set referencedClasses = new HashSet();
+
+ // Cached names on found system classes
+ private Set foundSystemClasses = new HashSet();
+
+ // Cached warning messages
+ private String[] staticRobotInstanceWarning;
+
+ public RobotClassLoader(URL robotClassPath, String robotFullClassName) {
+ super(new URL[] { robotClassPath}, Container.systemLoader);
+ fullClassName = robotFullClassName;
+ parent = getParent();
+ try {
+ codeSource = new CodeSource(new URL(UNTRUSTED_URL), (Certificate[]) null);
+ } catch (MalformedURLException ignored) {}
+ }
+
+ public void setRobotProxy(Object robotProxy) {
+ this.robotProxy = (IHostedThread) robotProxy;
+ }
+
+ public synchronized void addURL(URL url) {
+ super.addURL(url);
+ }
+
+ public synchronized Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if (name.startsWith("java.lang")) {
+ // we always delegate java.lang stuff to parent loader
+ return super.loadClass(name, resolve);
+ }
+ if (RobocodeProperties.isSecurityOn()) {
+ testPackages(name);
+ }
+ if (!name.startsWith("robocode")) {
+ Class> result = loadRobotClassLocaly(name, resolve);
+ if (result != null) {
+ // yes, it is in robot's class path
+ // we loaded it locally
+ return result;
+ }
+ }
+
+ // it is robot API
+ // or java class
+ // or security is off
+ // so we delegate to parent class loader
+ return parent.loadClass(name);
+ }
+
+ private void testPackages(String name) throws ClassNotFoundException {
+ if (name.startsWith("net.sf.robocode")) {
+ String message = "Robots are not allowed to reference Robocode engine in package: net.sf.robocode";
+
+ punishSecurityViolation(message);
+ throw new ClassNotFoundException(message);
+ }
+ if (name.startsWith("robocode.control")) {
+ String message = "Robots are not allowed to reference Robocode engine in package: robocode.control";
+
+ punishSecurityViolation(message);
+ throw new ClassNotFoundException(message);
+ }
+ if (RobocodeProperties.isSecurityOn() && name.startsWith("javax.swing")) {
+ String message = "Robots are not allowed to reference Robocode engine in package: javax.swing";
+
+ punishSecurityViolation(message);
+ throw new ClassNotFoundException(message);
+ }
+ }
+
+ private Class> loadRobotClassLocaly(String name, boolean resolve) throws ClassNotFoundException {
+ Class> result = findLoadedClass(name);
+ if (result == null) {
+ ByteBuffer resource = findLocalResource(name);
+ if (resource != null) {
+ result = defineClass(name, resource, codeSource);
+ if (resolve) {
+ resolveClass(result);
+ }
+ ClassAnalyzer.getReferencedClasses(resource, referencedClasses);
+ }
+ }
+ return result;
+ }
+
+ // this whole fun is there to be able to provide defineClass with bytes
+ // we need to call defineClass to be able to set codeSource to untrustedLocation
+ private ByteBuffer findLocalResource(final String name) {
+ return AccessController.doPrivileged(new PrivilegedAction() {
+ public ByteBuffer run() {
+ // try to find it in robot's class path
+ // this is URL, don't change to File.pathSeparator
+ String path = name.replace('.', '/').concat(".class");
+ URL url = findResource(path);
+ ByteBuffer result = null;
+ InputStream is = null;
+ BufferedInputStream bis = null;
+
+ if (url != null) {
+ try {
+ URLConnection connection = URLJarCollector.openConnection(url);
+
+ is = connection.getInputStream();
+ bis = new BufferedInputStream(is);
+
+ result = ByteBuffer.allocate(1024 * 8);
+ boolean done = false;
+
+ do {
+ do {
+ int res = bis.read(result.array(), result.position(), result.remaining());
+
+ if (res == -1) {
+ done = true;
+ break;
+ }
+ result.position(result.position() + res);
+ } while (result.remaining() != 0);
+ result.flip();
+ if (!done) {
+ result = ByteBuffer.allocate(result.capacity() * 2).put(result);
+ }
+ } while (!done);
+
+ } catch (IOException e) {
+ Logger.logError(e);
+ return null;
+ } finally {
+ FileUtil.cleanupStream(bis);
+ FileUtil.cleanupStream(is);
+ }
+ }
+ return result;
+ }
+ });
+ }
+
+ private void punishSecurityViolation(String message) {
+ if (robotProxy != null) {
+ robotProxy.punishSecurityViolation(message);
+ }
+ }
+
+ protected PermissionCollection getPermissions(CodeSource codesource) {
+ if (RobocodeProperties.isSecurityOn()) {
+ return EMPTY_PERMISSIONS;
+ }
+ return super.getPermissions(codesource);
+ }
+
+ public String[] getReferencedClasses() {
+ return referencedClasses.toArray(new String[referencedClasses.size()]);
+ }
+
+ public synchronized Class> loadRobotMainClass(boolean resolve) throws ClassNotFoundException {
+ try {
+ if (robotClass == null) {
+ robotClass = loadClass(fullClassName, resolve);
+
+ if (!IBasicRobot.class.isAssignableFrom(robotClass)) {
+ // that's not robot
+ return null;
+ }
+ if (resolve) {
+ // resolve methods to see more referenced classes
+ robotClass.getMethods();
+
+ // iterate thru dependencies until we didn't found any new
+ HashSet clone;
+
+ do {
+ clone = new HashSet(referencedClasses);
+ for (String reference : clone) {
+ testPackages(reference);
+ if (!isSystemClass(reference)) {
+ loadClass(reference, true);
+ }
+ }
+ } while (referencedClasses.size() != clone.size());
+ }
+ } else {
+ warnIfStaticRobotInstanceFields();
+ }
+ } catch (RuntimeException e) {
+ robotClass = null;
+ throw new ClassNotFoundException(e.getMessage(), e);
+ }
+ return robotClass;
+ }
+
+ public IBasicRobot createRobotInstance() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
+ loadRobotMainClass(true);
+ return (IBasicRobot) robotClass.newInstance();
+ }
+
+ public void cleanup() {
+ // Bug fix [2930266] - Robot static data isn't being GCed after battle
+ for (String className : getReferencedClasses()) {
+ cleanStaticReferences(className);
+ }
+
+ parent = null;
+ codeSource = null;
+ robotProxy = null;
+ robotClass = null;
+ referencedClasses = null;
+ foundSystemClasses = null;
+ }
+
+ /**
+ * Cleans all static field references on a class.
+ *
+ * @param className the name of the class containing the static references to clean.
+ */
+ private void cleanStaticReferences(String className) {
+ if (isSystemClass(className)) {
+ return;
+ }
+ Class> type = null;
+ try {
+ type = loadRobotClassLocaly(className, false);
+ if (type != null) {
+ for (Field field : getAllFields(new ArrayList(), type)) {
+ if (isStaticReference(field)) {
+ cleanStaticReference(field);
+ }
+ }
+ }
+ } catch (RuntimeException t) {
+ Logger.logError(t);
+ }
+ }
+
+ private void warnIfStaticRobotInstanceFields() {
+ if (staticRobotInstanceWarning == null) {
+ List staticRobotReferences = new ArrayList();
+
+ for (String className : getReferencedClasses()) { // Bug fix [3028102] - ConcurrentModificationException
+ if (isSystemClass(className)) {
+ continue;
+ }
+ Class> type = null;
+ try {
+ type = loadRobotClassLocaly(className, false);
+ } catch (RuntimeException t) {
+ continue;
+ }
+ if (type != null) {
+ for (Field field : getAllFields(new ArrayList(), type)) {
+ if (isStaticReference(field) && IBasicRobot.class.isAssignableFrom(field.getType())
+ && field.getAnnotation(robocode.annotation.SafeStatic.class) == null) {
+ staticRobotReferences.add(field);
+ }
+ }
+ }
+ }
+ if (staticRobotReferences.size() > 0) {
+ StringBuilder buf = new StringBuilder();
+
+ buf.append("Warning: ").append(fullClassName).append(
+ " uses static reference to a robot with the following field(s):");
+
+ for (Field field : staticRobotReferences) {
+ buf.append("\n\t").append(field.getDeclaringClass().getName()).append('.').append(field.getName()).append(", which points to a ").append(
+ field.getType().getName());
+ }
+
+ staticRobotInstanceWarning = new String[] {
+ buf.toString(),
+ "Static references to robots can cause unwanted behaviour with the robot using these.",
+ "Please change static robot references to non-static references and recompile the robot."};
+ } else {
+ staticRobotInstanceWarning = new String[] {}; // Signal that there is no warnings to cache
+ }
+ } else if (staticRobotInstanceWarning.length == 0) {
+ return; // Return, as no warnings should be written out in the robot console
+ }
+
+ // Write out warnings to the robot console
+ if (robotProxy != null) {
+ for (String line : staticRobotInstanceWarning) {
+ robotProxy.getOut().println("SYSTEM: " + line);
+ }
+ }
+ }
+
+ /**
+ * Cleans a static field reference class, even when it is 'private static final'
+ *
+ * @param field the field to clean, if it is a static reference.
+ */
+ private void cleanStaticReference(Field field) {
+ if (field.getName().startsWith("const__")) {
+ // Ignore 'const__' fields used with e.g. the Clojure language
+ return;
+ }
+ field.setAccessible(true);
+ try {
+ // In order to set a 'private static field', we need to fix the modifier, i.e. use magic! ;-)
+ Field modifiersField = Field.class.getDeclaredField("modifiers");
+ modifiersField.setAccessible(true);
+ int modifiers = modifiersField.getInt(field);
+ modifiersField.setInt(field, modifiers & ~Modifier.FINAL); // Remove the FINAL modifier
+ field.set(null, null);
+
+ } catch (RuntimeException ignore) {}
+ }
+
+ /**
+ * Gets all fields of a class (public, protected, private) and the ones inherited from all super classes.
+ * @param fields the list where the fields will be added as a result of calling this method.
+ * @param type the class to retrieve all the fields from
+ * @return the list specified as input parameter containing all the retrieved fields
+ */
+ private List getAllFields(List fields, Class> type) {
+ if (type == null || isSystemClass(type.getName())) {
+ return fields;
+ }
+ try {
+ for (Field field: type.getDeclaredFields()) {
+ fields.add(field);
+ }
+ } catch (RuntimeException ignore) {// NoClassDefFoundError does occur with some robots, e.g. sgp.Drunken [1.12]
+ // We ignore all exceptions and errors here so we can proceed to retrieve
+ // field from super classes.
+ }
+ if (type.getSuperclass() != null) {
+ fields = getAllFields(fields, type.getSuperclass());
+ }
+ return fields;
+ }
+
+ /**
+ * Checks if a specified class name is a Java system class or internal Robocode class.
+ * @param className the class name to check.
+ * @return true if the class name is a system class; false otherwise.
+ */
+ private boolean isSystemClass(String className) {
+ boolean isSystemClass = foundSystemClasses.contains(className);
+ if (!isSystemClass) {
+ try {
+ if (findSystemClass(className) != null) {
+ foundSystemClasses.add(className);
+ isSystemClass = true;
+ }
+ } catch (ClassNotFoundException ignore) {}
+ }
+ return isSystemClass;
+ }
+
+ /**
+ * Checks if a specified field is a static reference.
+ *
+ * @param field the field to check.
+ * @return true if the field is static reference; false otherwise.
+ */
+ private static boolean isStaticReference(Field field) {
+ return Modifier.isStatic(field.getModifiers())
+ && !(field.getType().isPrimitive() || field.isEnumConstant() || field.isSynthetic());
+ }
+}
diff --git a/代码/workspace_robo4/robocode.installer/src/main/java/net/sf/robocode/installer/AutoExtract.java b/代码/workspace_robo4/robocode.installer/src/main/java/net/sf/robocode/installer/AutoExtract.java
new file mode 100644
index 0000000..e4c6d0a
--- /dev/null
+++ b/代码/workspace_robo4/robocode.installer/src/main/java/net/sf/robocode/installer/AutoExtract.java
@@ -0,0 +1,705 @@
+/**
+ * Copyright (c) 2001-2016 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.installer;
+
+
+import javax.swing.*;
+
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.*;
+import java.net.URL;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
+
+
+/**
+ * Installer for Robocode.
+ *
+ * @author Mathew A. Nelsen (original)
+ * @author Flemming N. Larsen (contributor)
+ */
+public class AutoExtract implements ActionListener {
+ private JDialog licenseDialog;
+ private boolean accepted;
+ private static final String[] SPINNER = { "-", "\\", "|", "/"};
+ private String message = "";
+ private static File installDir;
+ private static final String osName = System.getProperty("os.name");
+ private static final double osVersion = doubleValue(System.getProperty("os.version"));
+ private static final String javaVersion = System.getProperty("java.version");
+
+ private static double doubleValue(String s) {
+ s = s.replaceAll("[^.0-9]", ""); // Remove invalid characters, e.g. "3.0-ARCH" become "3.0"
+
+ int p = s.indexOf(".");
+
+ if (p >= 0) {
+ p = s.indexOf(".", p + 1);
+ }
+ if (p >= 0) {
+ s = s.substring(0, p);
+ }
+
+ double d = 0.0;
+
+ try {
+ d = Double.parseDouble(s);
+ } catch (NumberFormatException e) {
+ e.printStackTrace(System.err);
+ }
+
+ return d;
+ }
+
+ private boolean acceptLicense() {
+ StringBuffer licenseText = new StringBuffer();
+
+ InputStream is;
+
+ try {
+ JarFile extractJar = new JarFile("extract.jar");
+
+ is = extractJar.getInputStream(extractJar.getJarEntry("license/cpl-v10.html"));
+ } catch (IOException e) {
+ return true;
+ }
+ if (is == null) {
+ return true;
+ }
+
+ InputStreamReader isr = null;
+ BufferedReader br = null;
+
+ try {
+ isr = new InputStreamReader(is);
+ br = new BufferedReader(isr);
+
+ String line = br.readLine();
+
+ while (line != null) {
+ licenseText.append(line);
+ line = br.readLine();
+ }
+ return acceptReject(licenseText.toString());
+
+ } catch (IOException e) {
+ System.err.println("Could not read line from license file: " + e);
+ } finally {
+ if (br != null) {
+ try {
+ br.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if (isr != null) {
+ try {
+ isr.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ return true;
+ }
+
+ private boolean acceptReject(String text) {
+ Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+
+ licenseDialog = new JDialog();
+ licenseDialog.setTitle("License Agreement");
+ licenseDialog.setModal(true);
+ licenseDialog.setLocation((screenSize.width - 500) / 2, (screenSize.height - 400) / 2);
+ licenseDialog.setSize(500, 400);
+ JTextPane t = new JTextPane();
+
+ t.setContentType("text/html");
+ t.setText(text);
+ t.setFont(new Font("Dialog", Font.PLAIN, 12));
+ t.setEditable(false);
+
+ JScrollPane s = new JScrollPane();
+
+ s.setViewportView(t);
+
+ licenseDialog.getContentPane().setLayout(new BorderLayout());
+ licenseDialog.getContentPane().add(s, BorderLayout.CENTER);
+
+ JPanel p = new JPanel();
+
+ p.setLayout(new BorderLayout());
+ JButton b1 = new JButton("Accept");
+ JButton b2 = new JButton("Cancel");
+
+ p.add(b1, BorderLayout.WEST);
+ p.add(b2, BorderLayout.EAST);
+
+ b1.addActionListener(this);
+ b2.addActionListener(this);
+
+ licenseDialog.getContentPane().add(p, BorderLayout.SOUTH);
+
+ licenseDialog.setVisible(true);
+
+ return accepted;
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ accepted = e.getActionCommand().equals("Accept");
+ licenseDialog.dispose();
+ licenseDialog = null;
+ }
+
+ private boolean extract(File installDir) {
+ JDialog statusDialog = new JDialog();
+ Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+
+ int width = 500;
+ int height = 100;
+
+ statusDialog.setTitle("Installing");
+ statusDialog.setLocation((screenSize.width - width) / 2, (screenSize.height - height) / 2);
+ statusDialog.setSize(width, height);
+ JLabel status = new JLabel();
+
+ statusDialog.getContentPane().setLayout(new BorderLayout());
+ statusDialog.getContentPane().add(status, BorderLayout.CENTER);
+
+ statusDialog.setVisible(true);
+
+ FileOutputStream fos;
+ String entryName;
+
+ byte buf[] = new byte[2048];
+
+ final String name = AutoExtract.class.getName().replaceAll("\\.", "/") + ".class";
+ String urlJar = AutoExtract.class.getClassLoader().getResource(name).toString();
+ final String src = urlJar.substring("jar:file:/".length(), urlJar.indexOf("!/"));
+
+ if (src.indexOf('!') > -1) {
+ message = src + "\nContains an exclamation point. Please move the file to a different directory.";
+ JOptionPane.showMessageDialog(null, message, "Error", JOptionPane.ERROR_MESSAGE);
+ return false;
+ }
+ JarInputStream jarIS = null;
+
+ try {
+ final URL url = new URL("file:/" + src);
+ InputStream is = url.openStream();
+
+ jarIS = new JarInputStream(is);
+
+ JarEntry entry;
+
+ while ((entry = jarIS.getNextJarEntry()) != null) {
+ int spin = 0;
+
+ entryName = entry.getName();
+ if (entry.isDirectory()) {
+ if (!entryName.startsWith("net") && !(entryName.equals("desktop") && isFreeBSD())) {
+ File dir = new File(installDir, entry.getName());
+ if (!dir.exists() && !dir.mkdirs()) {
+ System.err.println("Can't create dir: " + dir);
+ }
+ }
+ } else if (!entryName.equals(name)) {
+
+ // Skip .bat, .sh, and .command files depending on which OS Robocode is installed
+
+ final String entryNameLC = entryName.toLowerCase();
+
+ boolean skipEntry = false;
+
+ final boolean isBatFile = entryNameLC.length() > ".bat".length() && entryNameLC.endsWith(".bat");
+ final boolean isShFile = entryNameLC.length() > ".sh".length() && entryNameLC.endsWith(".sh");
+ final boolean isCommandFile = entryNameLC.length() > ".command".length()
+ && entryNameLC.endsWith(".command");
+ final boolean isDesktopFile = entryNameLC.startsWith("desktop/");
+
+ // Unix systems and Mac OS X
+ if (File.separatorChar == '/') {
+ // Skip .bat files under Unix and Mac OS X
+ // Skip .command files under Unix
+ skipEntry = isBatFile || (isCommandFile && !isMacOSX()) || (isDesktopFile && !isFreeBSD());
+ } else {
+ // Under Windows the .sh and .command files are skipped
+ skipEntry = isShFile || isCommandFile || isDesktopFile;
+ }
+
+ // If we are not skipping the entry, then copy from our .jar into the installation dir
+ if (!skipEntry) {
+ status.setText(entryName + " " + SPINNER[spin++]);
+
+ File out = new File(installDir, entryName);
+
+ File parentDirectory = new File(out.getParent());
+ if (!parentDirectory.exists() && !parentDirectory.mkdirs()) {
+ System.err.println("Can't create dir: " + parentDirectory);
+ }
+ fos = new FileOutputStream(out);
+
+ int index = 0;
+ int num;
+ int count = 0;
+
+ while ((num = jarIS.read(buf, 0, 2048)) != -1) {
+ fos.write(buf, 0, num);
+ index += num;
+ count++;
+ if (count > 80) {
+ status.setText(entryName + " " + SPINNER[spin++] + " (" + index + " bytes)");
+ if (spin > 3) {
+ spin = 0;
+ }
+ count = 0;
+ }
+ }
+ fos.close();
+
+ // Set file permissions for .sh and .command files under Unix and Mac OS X
+ if (File.separatorChar == '/') {
+ if (isShFile || isCommandFile) {
+ // Grant read and execute access for everyone and also write access for the owner of the file
+ Runtime.getRuntime().exec("chmod 755 " + out.toString());
+ }
+ }
+
+ status.setText(entryName + " " + SPINNER[spin] + " (" + index + " bytes)");
+ }
+ }
+ }
+ statusDialog.dispose();
+ return true;
+ } catch (IOException e) {
+ message = "Installation failed: " + e;
+ JOptionPane.showMessageDialog(null, message, "Error", JOptionPane.ERROR_MESSAGE);
+ return false;
+ } finally {
+ if (jarIS != null) {
+ try {
+ jarIS.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public static void main(String argv[]) {
+ String suggestedDirName;
+
+ if (argv.length == 1) {
+ suggestedDirName = argv[0];
+ } else if (isWindowsOS()) {
+ suggestedDirName = "C:\\robocode\\";
+ } else {
+ suggestedDirName = System.getProperty("user.home") + File.separator + "robocode" + File.separator;
+ }
+
+ String message;
+
+ if (install(new File(suggestedDirName))) {
+ message = "Installation successful";
+ } else {
+ message = "Installation cancelled";
+ }
+
+ // Delete the class file with the installer and it's parent folders in the robocode home dir
+ if (installDir != null) {
+ String installerPath = AutoExtract.class.getName().replaceAll("\\.", "/") + "$1.class";
+
+ deleteFileAndParentDirsIfEmpty(new File(installDir, installerPath));
+ }
+
+ JOptionPane.showMessageDialog(null, message);
+ }
+
+ private static boolean install(File suggestedDir) {
+ // Verify that the Java version is version 6 (1.6.0) or newer
+ if (javaVersion.startsWith("1.") && javaVersion.charAt(2) < '5') {
+ final String message = "Robocode requires Java 6 (1.6.0) or newer.\n"
+ + "Your system is currently running Java " + javaVersion + ".\n"
+ + "If you have not installed (or activated) at least\n" + "JRE 6 or JDK 6, please do so.";
+
+ JOptionPane.showMessageDialog(null, message, "Error", JOptionPane.ERROR_MESSAGE);
+ System.err.println(message);
+ return false;
+ }
+
+ // Set native look and feel
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (RuntimeException t) {// For some reason Ubuntu 7 can cause a NullPointerException when trying to getting the LAF
+ }
+
+ AutoExtract extractor = new AutoExtract();
+
+ if (extractor.acceptLicense()) {
+ boolean done = false;
+
+ while (!done) {
+ int rc = JOptionPane.showConfirmDialog(null,
+ "Robocode will be installed in:\n" + suggestedDir + "\nIs this ok?", "Installing Robocode",
+ JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
+
+ if (rc == JOptionPane.YES_OPTION) {
+ installDir = suggestedDir;
+ done = true;
+ } else if (rc == JOptionPane.NO_OPTION) {
+ Object r = JOptionPane.showInputDialog(null, "Please type in the installation directory",
+ "Installation Directory", JOptionPane.PLAIN_MESSAGE, null, null, suggestedDir);
+
+ if (r != null) {
+ suggestedDir = new File(((String) r).trim());
+ } else {
+ return false;
+ }
+ } else if (rc == JOptionPane.CANCEL_OPTION) {
+ return false;
+ }
+ }
+ if (!installDir.exists() && !installDir.mkdirs()) {
+ System.err.println("Can't create dir: " + installDir);
+ return false;
+ }
+ deleteOldLibs(installDir);
+
+ // The .robotcache has been renamed to .data
+ File robotsCacheDir = new File(installDir, "robots/.robotcache");
+ File robotsDataDir = new File(installDir, "robots/.data");
+
+ if (robotsCacheDir.exists()) {
+ // Rename ".robotcache" into ".data"
+ robotsCacheDir.renameTo(robotsDataDir);
+ }
+
+ // Fix problem with .data starting with a underscore dir by
+ // renaming files containing ".data/_" into ".data"
+ if (robotsDataDir.exists()) {
+ File underScoreDir = new File(robotsDataDir, "_");
+ String[] list = underScoreDir.list();
+
+ if (list != null) {
+ for (String fileName : list) {
+ File file = new File(underScoreDir, fileName);
+
+ file.renameTo(new File(robotsDataDir, fileName));
+ }
+ underScoreDir.delete();
+ }
+ }
+
+ // Create shortcuts and file associations
+ if (extractor.extract(installDir)) {
+ extractor.createShortcuts(installDir, "robocode.bat", "Robocode", "Robocode");
+ extractor.createFileAssociations(installDir);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static void deleteOldLibs(File installDir) {
+ File libs = new File(installDir, "libs");
+
+ if (libs.exists()) {
+ final File[] del = libs.listFiles(new FilenameFilter() {
+
+ public boolean accept(File dir, String name) {
+ String test = name.toLowerCase();
+
+ return test.endsWith(".jar") || test.endsWith(".dll");
+ }
+ });
+
+ for (File d : del) {
+ if (!d.delete()) {
+ System.err.println("Can't delete: " + d);
+ }
+ }
+ }
+ }
+
+ private static boolean deleteDir(File dir) {
+ if (dir.isDirectory()) {
+ for (File file : dir.listFiles()) {
+ if (file.isDirectory()) {
+ // Skip directories ending with ".data"
+ if (file.getName().endsWith(".data")) {
+ continue;
+ }
+ try {
+ // Test for symlink and ignore.
+ // Robocode won't create one, but just in case a user does...
+ if (file.getCanonicalFile().getParentFile().equals(dir.getCanonicalFile())) {
+ deleteDir(file);
+ if (file.exists() && !file.delete()) {
+ System.err.println("Can't delete: " + file);
+ }
+ } else {
+ System.out.println("Warning: " + file + " may be a symlink. It has been ignored");
+ }
+ } catch (IOException e) {
+ System.out.println(
+ "Warning: Cannot determine canonical file for " + file + ". It has been ignored");
+ }
+ } else {
+ if (file.exists() && !file.delete()) {
+ System.err.println("Can't delete: " + file);
+ }
+ }
+ }
+ return dir.delete();
+ }
+ return false;
+ }
+
+ private void createShortcuts(File installDir, String runnable, String folder, String name) {
+ if (osName.toLowerCase().indexOf("win") == 0) {
+ if (createWindowsShortcuts(installDir, runnable, folder, name) == false) {
+ JOptionPane.showMessageDialog(null,
+ message + "\n" + "To start Robocode, enter the following at a command prompt:\n" + "cd "
+ + installDir.getAbsolutePath() + "\n" + "robocode.bat");
+ }
+ } else if (osName.toLowerCase().indexOf("mac") == 0) {
+ if (osVersion >= 10.1) {
+ JOptionPane.showMessageDialog(null,
+ message + "\n" + "To start Robocode, browse to " + installDir + " then double-click robocode.sh\n");
+ } else {
+ JOptionPane.showMessageDialog(null,
+ message + "\n" + "To start Robocode, enter the following at a command prompt:\n"
+ + installDir.getAbsolutePath() + "/robocode.sh");
+ }
+ } else {
+ JOptionPane.showMessageDialog(null,
+ message + "\n" + "To start Robocode, enter the following at a command prompt:\n"
+ + installDir.getAbsolutePath() + "/robocode.sh");
+ }
+ }
+
+ private boolean createWindowsShortcuts(File installDir, String runnable, String folder, String name) {
+ int rc = JOptionPane.showConfirmDialog(null,
+ "Would you like to install a shortcut to Robocode in the Start menu? (Recommended)", "Create Shortcuts",
+ JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
+
+ if (rc != JOptionPane.YES_OPTION) {
+ return false;
+ }
+
+ final String command = getWindowsCmd() + " cscript.exe ";
+
+ try {
+ File shortcutMaker = new File(installDir, "makeshortcut.vbs");
+ PrintStream out = new PrintStream(new FileOutputStream(shortcutMaker));
+
+ out.println("WScript.Echo(\"Creating shortcuts...\")");
+ out.println("Set Shell = CreateObject (\"WScript.Shell\")");
+ out.println("Set fso = CreateObject(\"Scripting.FileSystemObject\")");
+ out.println("ProgramsPath = Shell.SpecialFolders(\"Programs\")");
+ out.println("if (not(fso.folderExists(ProgramsPath + \"\\\\" + folder + "\"))) Then");
+ out.println(" fso.CreateFolder(ProgramsPath + \"\\\\" + folder + "\")");
+ out.println("End If");
+ out.println("Set link = Shell.CreateShortcut(ProgramsPath + \"\\\\" + folder + "\\\\" + name + ".lnk\")");
+ out.println("link.Arguments = \"\"");
+ out.println("link.Description = \"" + name + "\"");
+ out.println("link.HotKey = \"\"");
+ out.println("link.IconLocation = \"" + escaped(installDir.getAbsolutePath()) + "\\\\" + "robocode.ico,0\"");
+ out.println("link.TargetPath = \"" + escaped(installDir.getAbsolutePath()) + "\\\\" + runnable + "\"");
+ out.println("link.WindowStyle = 1");
+ out.println("link.WorkingDirectory = \"" + escaped(installDir.getAbsolutePath()) + "\"");
+ out.println("link.Save()");
+ out.println("DesktopPath = Shell.SpecialFolders(\"Desktop\")");
+ out.println("Set link = Shell.CreateShortcut(DesktopPath + \"\\\\" + name + ".lnk\")");
+ out.println("link.Arguments = \"\"");
+ out.println("link.Description = \"" + name + "\"");
+ out.println("link.HotKey = \"\"");
+ out.println("link.IconLocation = \"" + escaped(installDir.getAbsolutePath()) + "\\\\" + "robocode.ico,0\"");
+ out.println("link.TargetPath = \"" + escaped(installDir.getAbsolutePath()) + "\\\\" + runnable + "\"");
+ out.println("link.WindowStyle = 1");
+ out.println("link.WorkingDirectory = \"" + escaped(installDir.getAbsolutePath()) + "\"");
+ out.println("link.Save()");
+ out.println("WScript.Echo(\"Shortcuts created.\")");
+
+ out.close();
+
+ Process p = Runtime.getRuntime().exec(command + " makeshortcut.vbs", null, installDir);
+ int rv = p.waitFor();
+
+ if (rv != 0) {
+ System.err.println("Can't create shortcut: " + shortcutMaker);
+ return false;
+ }
+ JOptionPane.showMessageDialog(null,
+ message + "\n" + "A Robocode program group has been added to your Start menu\n"
+ + "A Robocode icon has been added to your desktop.");
+ if (!shortcutMaker.delete()) {
+ System.err.println("Can't delete: " + shortcutMaker);
+ }
+ return true;
+ } catch (IOException e) {
+ e.printStackTrace(System.err);
+ } catch (InterruptedException e) {
+ e.printStackTrace(System.err);
+ }
+
+ return false;
+ }
+
+ private void createFileAssociations(File installDir) {
+ if (isWindowsOS()) {
+ createWindowsFileAssociations(installDir);
+ }
+ }
+
+ private void createWindowsFileAssociations(File installDir) {
+ int rc = JOptionPane.showConfirmDialog(null,
+ "Would you like to create file associations for Robocode in\n"
+ + "the Windows Registry for the file extensions .battle and .br?\n"
+ + "Please notice that you might need to grant permission to add\n"
+ + "the file associations in the Registry, and you must be an\n"
+ + "administrator or granted permission to change the registry.",
+ "Create File Associations",
+ JOptionPane.YES_NO_OPTION,
+ JOptionPane.QUESTION_MESSAGE);
+
+ if (rc != JOptionPane.YES_OPTION) {
+ return;
+ }
+
+ File file = null;
+ PrintStream out = null;
+
+ try {
+ file = new File(installDir + "\\FileAssoc.reg");
+
+ out = new PrintStream(new FileOutputStream(file));
+
+ String installPath = installDir.getAbsolutePath();
+
+ out.print(
+ createWindowsRegistryFileAssociation(installPath, ".battle", "Robocode.BattleSpecification",
+ "Robocode Battle Specification", "-battle"));
+ out.print(
+ createWindowsRegistryFileAssociation(installPath, ".br", "Robocode.BattleRecord", "Robocode Battle Record",
+ "-replay"));
+
+ out.close();
+ out = null;
+
+ Process p = Runtime.getRuntime().exec(getWindowsCmd() + file.getAbsolutePath(), null, null);
+ int rv;
+
+ try {
+ rv = p.waitFor();
+ if (rv != 0) {
+ System.err.println("Could not create association(s)");
+ }
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ if (out != null) {
+ out.close();
+ }
+ if (file != null) {
+ if (!file.delete()) {
+ System.err.println("Could not delete the file: " + file);
+ }
+ }
+ }
+ }
+
+ private static String createWindowsRegistryFileAssociation(String installDir, String fileExt, String progId, String description, String robocodeCmdParam) {
+ StringBuffer sb = new StringBuffer();
+
+ final String HKCR = "[HKEY_CLASSES_ROOT\\";
+
+ sb.append("REGEDIT4\n");
+ sb.append(HKCR).append(fileExt).append("]\n");
+ sb.append("@=\"").append(progId).append("\"\n");
+ sb.append(HKCR).append(progId).append("]\n");
+ sb.append("@=\"").append(description).append("\"\n");
+ sb.append(HKCR).append(progId).append("\\shell]\n");
+ sb.append(HKCR).append(progId).append("\\shell\\open]\n");
+ sb.append(HKCR).append(progId).append("\\shell\\open\\command]\n");
+ sb.append("@=\"").append(getWindowsCmd()).append("\\\"cd \\\"").append(installDir.replaceAll("[\\\\]", "\\\\\\\\")).append("\\\" && robocode.bat ").append(robocodeCmdParam).append(
+ " \\\"%1\\\"\\\"\"\n");
+
+ return sb.toString();
+ }
+
+ private static String escaped(String s) {
+ StringBuffer eascaped = new StringBuffer();
+
+ for (int i = 0; i < s.length(); i++) {
+ if (s.charAt(i) == '\\') {
+ eascaped.append('\\');
+ }
+ eascaped.append(s.charAt(i));
+ }
+ return eascaped.toString();
+ }
+
+ private static boolean isWindowsOS() {
+ return osName.startsWith("Windows ");
+ }
+
+ private static boolean isMacOSX() {
+ return osName.startsWith("Mac OS X");
+ }
+
+ private static boolean isFreeBSD() {
+ return osName.equals("FreeBSD");
+ }
+
+ private static String getWindowsCmd() {
+ String os = System.getProperty("os.name");
+
+ return ((os.equals("Windows 95") || os.equals("Windows 95") || os.equals("Windows ME"))
+ ? "command.com"
+ : "cmd.exe")
+ + " /C ";
+ }
+
+ /**
+ * Deletes a file and afterwards deletes the parent directories that are empty.
+ *
+ * @param file the file or directory to delete
+ * @return true if success
+ */
+ private static boolean deleteFileAndParentDirsIfEmpty(final File file) {
+ boolean wasDeleted = false;
+
+ if (file != null && file.exists()) {
+ if (file.isDirectory()) {
+ wasDeleted = deleteDir(file);
+ } else {
+ wasDeleted = file.delete();
+
+ File parent = file;
+
+ while (wasDeleted && (parent = parent.getParentFile()) != null) {
+ // Delete parent directory, but only if it is empty
+ File[] files = parent.listFiles();
+
+ if (files != null && files.length == 0) {
+ wasDeleted = deleteDir(parent);
+ } else {
+ wasDeleted = false;
+ }
+ }
+ }
+ }
+ return wasDeleted;
+ }
+}
diff --git a/代码/workspace_robo4/robocode.roborumble/src/main/java/net/sf/robocode/roborumble/netengine/FileTransfer.java b/代码/workspace_robo4/robocode.roborumble/src/main/java/net/sf/robocode/roborumble/netengine/FileTransfer.java
new file mode 100644
index 0000000..09004c4
--- /dev/null
+++ b/代码/workspace_robo4/robocode.roborumble/src/main/java/net/sf/robocode/roborumble/netengine/FileTransfer.java
@@ -0,0 +1,657 @@
+/**
+ * Copyright (c) 2001-2016 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.roborumble.netengine;
+
+
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Properties;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.InflaterInputStream;
+
+import static net.sf.robocode.roborumble.util.PropertiesUtil.getProperties;
+
+
+/**
+ * Utility class for downloading files from the net and copying files.
+ *
+ * @author Flemming N. Larsen (original)
+ */
+public class FileTransfer {
+
+ private final static int DEFAULT_CONNECTION_TIMEOUT = 10000; // 10 seconds
+ private final static int DEFAULT_READ_TIMEOUT = 10000; // 10 seconds
+ private final static int DEFAULT_SESSION_TIMEOUT = 10000; // 10 seconds
+
+ private static int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
+ private static int readTimeout = DEFAULT_READ_TIMEOUT;
+ private static int sessionTimeout = DEFAULT_SESSION_TIMEOUT;
+
+ static {
+ readProperties();
+ }
+
+ /**
+ * Represents the download status returned when downloading files.
+ */
+ public enum DownloadStatus {
+ OK, // The download was succesful
+ COULD_NOT_CONNECT, // Connection problem
+ FILE_NOT_FOUND, // The file to download was not found
+ }
+
+
+ /**
+ * Daemon worker thread containing a 'finish' flag for waiting and notifying when the thread has finished it's job.
+ */
+ private static class WorkerThread extends Thread {
+
+ final Object monitor = new Object();
+
+ volatile boolean isFinished;
+
+ public WorkerThread(String name) {
+ super(name);
+ setDaemon(true);
+ }
+
+ void notifyFinish() {
+ // Notify that this thread is finish
+ synchronized (monitor) {
+ isFinished = true;
+ monitor.notifyAll();
+ }
+ }
+ }
+
+ /*
+ * Returns a session id for keeping a session on a HTTP site.
+ *
+ * @param url is the url of the HTTP site.
+ *
+ * @return a session id for keeping a session on a HTTP site or null if no session is available.
+ */
+ public static String getSessionId(String url) {
+ HttpURLConnection conn = null;
+
+ try {
+ // Open connection
+ conn = FileTransfer.connectToHttpInputConnection(new URL(url));
+ if (conn == null) {
+ throw new IOException("Could not open connection to '" + url + "'");
+ }
+
+ // Get a session id if available
+ final GetSessionIdThread sessionIdThread = new GetSessionIdThread(conn);
+
+ sessionIdThread.start();
+
+ // Wait for the session id
+ synchronized (sessionIdThread.monitor) {
+ while (!sessionIdThread.isFinished) {
+ try {
+ sessionIdThread.monitor.wait(sessionTimeout);
+ sessionIdThread.interrupt();
+ } catch (InterruptedException e) {
+ // Immediately reasserts the exception by interrupting the caller thread itself
+ Thread.currentThread().interrupt();
+
+ return null;
+ }
+ }
+ }
+
+ // Return the session id
+ return sessionIdThread.sessionId;
+
+ } catch (final IOException e) {
+ return null;
+ } finally {
+ // Make sure the connection is disconnected.
+ // This will cause threads using the connection to throw an exception
+ // and thereby terminate if they were hanging.
+ if (conn != null) {
+ conn.disconnect();
+ }
+ }
+ }
+
+ /**
+ * Worker thread used for getting the session id of an already open HTTP connection.
+ */
+ private final static class GetSessionIdThread extends WorkerThread {
+
+ // The resulting session id to read out
+ String sessionId;
+
+ final HttpURLConnection conn;
+
+ GetSessionIdThread(HttpURLConnection conn) {
+ super("FileTransfer: Get session ID");
+ this.conn = conn;
+ }
+
+ @Override
+ public void run() {
+ try {
+ // Get the cookie value
+ final String cookieVal = conn.getHeaderField("Set-Cookie");
+
+ // Extract the session id from the cookie value
+ if (cookieVal != null) {
+ sessionId = cookieVal.substring(0, cookieVal.indexOf(";"));
+ }
+ } catch (final Exception e) {
+ sessionId = null;
+ }
+ // Notify that this thread is finish
+ notifyFinish();
+ }
+ }
+
+ /**
+ * Downloads a file from a HTTP site.
+ *
+ * @param url is the url of the HTTP site to download the file from.
+ * @param filename is the filename of the destination file.
+ * @param sessionId is an optional session id if the download is session based.
+ * @return the download status, which is DownloadStatus.OK if the download completed successfully; otherwise an
+ * error occurred.
+ */
+ public static DownloadStatus download(String url, String filename, String sessionId) {
+ HttpURLConnection conn = null;
+
+ try {
+ // Create connection
+ conn = connectToHttpInputConnection(new URL(url), sessionId);
+ if (conn == null) {
+ throw new IOException("Could not open connection to: " + url);
+ }
+
+ // Begin the download
+ final DownloadThread downloadThread = new DownloadThread(conn, filename);
+
+ downloadThread.start();
+
+ // Wait for the download to complete
+ synchronized (downloadThread.monitor) {
+ while (!downloadThread.isFinished) {
+ try {
+ downloadThread.monitor.wait();
+ } catch (InterruptedException e) {
+ return DownloadStatus.COULD_NOT_CONNECT;
+ }
+ }
+ }
+
+ // Return the download status
+ return downloadThread.status;
+
+ } catch (final IOException e) {
+ return DownloadStatus.COULD_NOT_CONNECT;
+ } finally {
+ // Make sure the connection is disconnected.
+ // This will cause threads using the connection to throw an exception
+ // and thereby terminate if they were hanging.
+ try {
+ if (conn != null) {
+ conn.disconnect();
+ }
+ } catch (RuntimeException ignore) {// we expect this, right ?
+ }
+ }
+ }
+
+ /**
+ * Worker thread used for downloading a file from an already open HTTP connection.
+ */
+ private final static class DownloadThread extends WorkerThread {
+
+ // The download status to be read out
+ DownloadStatus status = DownloadStatus.COULD_NOT_CONNECT; // Default error
+
+ final HttpURLConnection conn;
+ final String filename;
+
+ InputStream in;
+ OutputStream out;
+
+ DownloadThread(HttpURLConnection conn, String filename) {
+ super("FileTransfer: Download");
+ this.conn = conn;
+ this.filename = filename;
+ }
+
+ @Override
+ public void run() {
+ try {
+ // Start getting the response code
+ final GetResponseCodeThread responseThread = new GetResponseCodeThread(conn);
+
+ responseThread.start();
+
+ // Wait for the response to finish
+ synchronized (responseThread.monitor) {
+ while (!responseThread.isFinished) {
+ try {
+ responseThread.monitor.wait(sessionTimeout);
+ responseThread.interrupt();
+ } catch (InterruptedException e) {
+ notifyFinish();
+ return;
+ }
+ }
+ }
+
+ final int responseCode = responseThread.responseCode;
+
+ if (responseCode == -1) {
+ // Terminate if we did not get the response code
+ notifyFinish();
+ return;
+
+ } else if (responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
+ // Terminate if the HTTP page containing the file was not found
+ status = DownloadStatus.FILE_NOT_FOUND;
+ notifyFinish();
+ return;
+
+ } else if (responseCode != HttpURLConnection.HTTP_OK) {
+ // Generally, terminate if did not receive a OK response
+ notifyFinish();
+ return;
+ }
+
+ // Start getting the size of the file to download
+ final GetContentLengthThread contentLengthThread = new GetContentLengthThread(conn);
+
+ contentLengthThread.start();
+
+ // Wait for the file size
+ synchronized (contentLengthThread.monitor) {
+ while (!contentLengthThread.isFinished) {
+ try {
+ contentLengthThread.monitor.wait(sessionTimeout);
+ contentLengthThread.interrupt();
+ } catch (InterruptedException e) {
+ notifyFinish();
+ return;
+ }
+ }
+ }
+
+ final int size = contentLengthThread.contentLength;
+
+ if (size == -1) {
+ // Terminate if we did not get the content length
+ notifyFinish();
+ return;
+ }
+
+ // Get the input stream from the connection
+ in = getInputStream(conn);
+
+ // Prepare the output stream for the file output
+ out = new FileOutputStream(filename);
+
+ // Download the file
+
+ final byte[] buf = new byte[4096];
+
+ int totalRead = 0;
+ int bytesRead;
+
+ // Start thread for reading bytes into the buffer
+
+ while (totalRead < size) {
+ // Start reading bytes into the buffer
+ final ReadInputStreamToBufferThread readThread = new ReadInputStreamToBufferThread(in, buf);
+
+ readThread.start();
+
+ // Wait for the reading to finish
+ synchronized (readThread.monitor) {
+ while (!readThread.isFinished) {
+ try {
+ readThread.monitor.wait(sessionTimeout);
+ readThread.interrupt();
+ } catch (InterruptedException e) {
+ notifyFinish();
+ return;
+ }
+ }
+ }
+
+ bytesRead = readThread.bytesRead;
+ if (bytesRead == -1) {
+ // Read completed has completed
+ notifyFinish();
+ break;
+ }
+
+ // Write the byte buffer to the output
+ out.write(buf, 0, bytesRead);
+
+ totalRead += bytesRead;
+ }
+
+ // If we reached this point, the download was successful
+ status = DownloadStatus.OK;
+
+ notifyFinish();
+
+ } catch (final IOException e) {
+ status = DownloadStatus.COULD_NOT_CONNECT;
+ } finally {
+ // Make sure the input stream is closed
+ if (in != null) {
+ try {
+ in.close();
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ }
+ // Make sure the output stream is closed
+ if (out != null) {
+ try {
+ out.close();
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Worker thread used for getting the response code of an already open HTTP connection.
+ */
+ final static class GetResponseCodeThread extends WorkerThread {
+
+ // The response code to read out
+ int responseCode;
+
+ final HttpURLConnection conn;
+
+ GetResponseCodeThread(HttpURLConnection conn) {
+ super("FileTransfer: Get response code");
+ this.conn = conn;
+ }
+
+ @Override
+ public void run() {
+ try {
+ // Get the response code
+ responseCode = conn.getResponseCode();
+ } catch (final Exception e) {
+ responseCode = -1;
+ }
+ // Notify that this thread is finish
+ notifyFinish();
+ }
+ }
+
+
+ /**
+ * Worker thread used for getting the content length of an already open HTTP connection.
+ */
+ final static class GetContentLengthThread extends WorkerThread {
+
+ // The content length to read out
+ int contentLength;
+
+ final HttpURLConnection conn;
+
+ GetContentLengthThread(HttpURLConnection conn) {
+ super("FileTransfer: Get content length");
+ this.conn = conn;
+ }
+
+ @Override
+ public void run() {
+ try {
+ // Get the content length
+ contentLength = conn.getContentLength();
+ } catch (final Exception e) {
+ contentLength = -1;
+ }
+ // Notify that this thread is finish
+ notifyFinish();
+ }
+ }
+
+
+ /**
+ * Worker thread used for reading bytes from an already open input stream into a byte buffer.
+ */
+ final static class ReadInputStreamToBufferThread extends WorkerThread {
+
+ int bytesRead;
+
+ final InputStream in;
+
+ final byte[] buf;
+
+ ReadInputStreamToBufferThread(InputStream in, byte[] buf) {
+ super("FileTransfer: Read input stream to buffer");
+ this.in = in;
+ this.buf = buf;
+ }
+
+ @Override
+ public void run() {
+ try {
+ // Read bytes into the buffer
+ bytesRead = in.read(buf);
+ } catch (final Exception e) {
+ bytesRead = -1;
+ }
+ // Notify that this thread is finish
+ notifyFinish();
+ }
+ }
+
+ /**
+ * Copies a file into another file.
+ *
+ * @param srcFile is the filename of the source file to copy.
+ * @param destFile is the filename of the destination file to copy the file into.
+ * @return true if the file was copied; false otherwise
+ */
+ public static boolean copy(String srcFile, String destFile) {
+ FileInputStream in = null;
+ FileOutputStream out = null;
+
+ try {
+ if (srcFile.equals(destFile)) {
+ throw new IOException("You cannot copy a file onto itself");
+ }
+ final byte[] buf = new byte[4096];
+
+ in = new FileInputStream(srcFile);
+ out = new FileOutputStream(destFile);
+
+ while (in.available() > 0) {
+ out.write(buf, 0, in.read(buf, 0, buf.length));
+ }
+ } catch (final IOException e) {
+ return false;
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Opens and connects to a {@link java.net.HttpURLConnection} for input only, and where the connection timeout and
+ * read timeout are controlled by properties.
+ *
+ * @param url is the URL to open a connection to.
+ * @return a HttpURLConnection intended for reading input only.
+ * @throws IOException if an I/O exception occurs.
+ */
+ public static HttpURLConnection connectToHttpInputConnection(URL url) throws IOException {
+ return connectToHttpInputConnection(url, null);
+ }
+
+ /**
+ * Opens and connects to a {@link java.net.HttpURLConnection} for input only, and where the connection timeout and
+ * read timeout are controlled by properties.
+ *
+ * @param url is the URL to open a connection to.
+ * @param sessionId is a optional session id.
+ * @return a HttpURLConnection intended for reading input only.
+ * @throws IOException if an I/O exception occurs.
+ */
+ public static HttpURLConnection connectToHttpInputConnection(URL url, String sessionId) throws IOException {
+ HttpURLConnection conn = (HttpURLConnection) openURLConnection(url, false); // not for output
+
+ conn.setRequestMethod("GET");
+ if (sessionId != null) {
+ conn.setRequestProperty("Cookie", sessionId);
+ }
+ conn.connect();
+ return conn;
+ }
+
+ /**
+ * Opens a {link {@link java.net.URLConnection} for output (and input) where the connection timeout and read timeout
+ * are controlled by properties.
+ *
+ * @param url is the URL to open.
+ * @return a URLConnection for output.
+ * @throws IOException if an I/O exception occurs.
+ */
+ public static URLConnection openOutputURLConnection(URL url) throws IOException {
+ return openURLConnection(url, true); // for output
+ }
+
+ /**
+ * Convenient method used for getting an input stream from an URLConnection.
+ * This method checks if a GZIPInputStream or InflaterInputStream should be used to wrap the input stream from the
+ * URLConnection depending on the content encoding.
+ *
+ * @param conn is the URLConnection
+ * @return an input stream from the URLConnection, which can be a GZIPInputStream or InflaterInputStream.
+ * @throws IOException if an I/O exception occurs.
+ */
+ public static InputStream getInputStream(URLConnection conn) throws IOException {
+ // Get input stream
+ InputStream in = conn.getInputStream();
+
+ // Get the encoding returned by the server
+ String encoding = conn.getContentEncoding();
+
+ // Check if we need to use a gzip or inflater input stream depending on the content encoding
+ if ("gzip".equalsIgnoreCase(encoding)) {
+ in = new GZIPInputStream(in);
+ } else if ("deflate".equalsIgnoreCase(encoding)) {
+ in = new InflaterInputStream(in);
+ }
+ return in;
+ }
+
+ /**
+ * Convenient method used for getting an output stream from an URLConnection.
+ * This method checks if a GZIPOutputStream or DeflaterOutputStream should be used to wrap the output stream from
+ * the URLConnection depending on the content encoding.
+ *
+ * @param conn is the URLConnection
+ * @return an output stream from the URLConnection, which can be a GZIPOutputStream or DeflaterOutputStream.
+ * @throws IOException if an I/O exception occurs.
+ */
+ public static OutputStream getOutputStream(URLConnection conn) throws IOException {
+ // Get output stream
+ OutputStream out = conn.getOutputStream();
+
+ // // Get the encoding returned by the server
+ // String encoding = conn.getContentEncoding();
+ //
+ // // Check if we need to use a gzip or inflater input stream depending on the content encoding
+ // if ("gzip".equalsIgnoreCase(encoding)) {
+ // out = new GZIPOutputStream(out);
+ // } else if ("deflate".equalsIgnoreCase(encoding)) {
+ // out = new DeflaterOutputStream(out);
+ // }
+ return out;
+ }
+
+ /**
+ * Opens a {link {@link java.net.URLConnection} for input and optional output where the connection timeout and read
+ * timeout are controlled by properties.
+ *
+ * @param url is the URL to open.
+ * @param isOutput is a flag specifying if the opened connection is for output.
+ * @return a URLConnection.
+ * @throws IOException if an I/O exception occurs.
+ */
+ public static URLConnection openURLConnection(URL url, boolean isOutput) throws IOException {
+ URLConnection conn = url.openConnection();
+
+ conn.setDoInput(true);
+ conn.setDoOutput(isOutput);
+
+ conn.setConnectTimeout(connectionTimeout);
+ conn.setReadTimeout(readTimeout);
+
+ if (!isOutput) {
+ // Allow both GZip and Deflate (ZLib) encodings
+ conn.setRequestProperty("Accept-Encoding", "gzip, deflate");
+ conn.setRequestProperty("User-Agent", "RoboRumble@Home - gzip, deflate");
+ }
+ return conn;
+ }
+
+ /**
+ * Reads the roborumble.properties file and stores property values into global variables.
+ */
+ private static void readProperties() {
+ Properties props = getProperties("./roborumble/roborumble.properties");
+
+ // Get connection timeout
+ String value = props.getProperty("connection.open.timeout");
+
+ if (value != null) {
+ try {
+ connectionTimeout = Integer.parseInt(value);
+ } catch (NumberFormatException ignore) {}
+ }
+
+ // Get connection read timeout
+ value = props.getProperty("connection.read.timeout");
+ if (value != null) {
+ try {
+ readTimeout = Integer.parseInt(value);
+ } catch (NumberFormatException ignore) {}
+ }
+
+ // Get download session timeout
+ value = props.getProperty("download.session.timeout");
+ if (value != null) {
+ try {
+ sessionTimeout = Integer.parseInt(value);
+ } catch (NumberFormatException ignore) {}
+ }
+ }
+}
diff --git a/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/AwtAttack.java b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/AwtAttack.java
new file mode 100644
index 0000000..5722779
--- /dev/null
+++ b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/AwtAttack.java
@@ -0,0 +1,84 @@
+/**
+ * Copyright (c) 2001-2016 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 tested.robots;
+
+
+import robocode.AdvancedRobot;
+import robocode.ScannedRobotEvent;
+
+import javax.swing.*;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+
+/**
+ * @author Pavel Savara (original)
+ */
+public class AwtAttack extends AdvancedRobot {
+
+ @Override
+ public void run() {
+ // noinspection InfiniteLoopStatement
+ for (;;) {
+ turnLeft(100);
+ ahead(10);
+ turnLeft(100);
+ back(10);
+ }
+ }
+
+ @Override
+ public void onScannedRobot(ScannedRobotEvent event) {
+ awtAttack();
+ }
+
+ private void awtAttack() {
+ try {
+ Runnable doHack = new Runnable() {
+ public void run() {
+ writeAttack();
+
+ JFrame frame;
+
+ frame = new JFrame();
+ frame.setName("Hack");
+ frame.setVisible(true);
+
+ }
+ };
+
+ javax.swing.SwingUtilities.invokeLater(doHack);
+ } catch (RuntimeException e) {
+ // swalow security exception
+ e.printStackTrace(out);
+ }
+ }
+
+ private void writeAttack() {
+ FileOutputStream fs;
+
+ try {
+ fs = new FileOutputStream("C:\\Robocode.attack");
+ fs.write(0xBA);
+ fs.write(0xDF);
+ fs.write(0x00);
+ fs.write(0xD0);
+ fs.close();
+ out.println("Hacked!!!");
+ } catch (FileNotFoundException e) {
+ e.printStackTrace(out);
+ } catch (IOException e) {
+ e.printStackTrace(out);
+ } catch (RuntimeException e) {
+ // swalow security exception
+ e.printStackTrace(out);
+ }
+ }
+
+}
diff --git a/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ConstructorAwtAttack.java b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ConstructorAwtAttack.java
new file mode 100644
index 0000000..47ae244
--- /dev/null
+++ b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ConstructorAwtAttack.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2001-2016 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 tested.robots;
+
+
+import javax.swing.*;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+
+/**
+ * @author Flemming N. Larsen (original)
+ */
+public class ConstructorAwtAttack extends robocode.AdvancedRobot {
+
+ public ConstructorAwtAttack() {
+ awtAttack();
+ }
+
+ private void awtAttack() {
+ try {
+ Runnable doHack = new Runnable() {
+ public void run() {
+ writeAttack();
+
+ JFrame frame;
+
+ frame = new JFrame();
+ frame.setName("Hack");
+ frame.setVisible(true);
+
+ }
+ };
+
+ javax.swing.SwingUtilities.invokeLater(doHack);
+ } catch (RuntimeException e) {
+ // swallow security exception
+ e.printStackTrace(out);
+ }
+ }
+
+ private void writeAttack() {
+ FileOutputStream fs;
+
+ try {
+ fs = new FileOutputStream("C:\\Robocode.attack");
+ fs.write(0xBA);
+ fs.write(0xDF);
+ fs.write(0x00);
+ fs.write(0xD0);
+ fs.close();
+ out.println("Hacked!!!");
+ } catch (FileNotFoundException e) {
+ e.printStackTrace(out);
+ } catch (IOException e) {
+ e.printStackTrace(out);
+ } catch (RuntimeException e) {
+ // swallow security exception
+ e.printStackTrace(out);
+ }
+ }
+}
diff --git a/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ConstructorThreadAttack.java b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ConstructorThreadAttack.java
new file mode 100644
index 0000000..0c5a0f8
--- /dev/null
+++ b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ConstructorThreadAttack.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2001-2016 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 tested.robots;
+
+
+/**
+ * @author Flemming N. Larsen (original)
+ */
+public class ConstructorThreadAttack extends robocode.AdvancedRobot {
+
+ public ConstructorThreadAttack() {
+ runAttack();
+ runAttack2();
+ }
+
+ private void runAttack() {
+ try {
+ Attacker a = new Attacker();
+ Thread t = new Thread(a);
+
+ t.start();
+ } catch (RuntimeException e) {
+ // swallow security exception
+ e.printStackTrace(out);
+ }
+ }
+
+ private void runAttack2() {
+ try {
+ Attacker a = new Attacker();
+ ThreadGroup tg = new ThreadGroup("MyAttack");
+
+ tg.setMaxPriority(10);
+ Thread t = new Thread(tg, a);
+
+ t.start();
+ } catch (RuntimeException e) {
+ // swallow security exception
+ e.printStackTrace(out);
+ }
+ }
+
+ private class Attacker implements Runnable {
+
+ public synchronized void run() {
+ if (Thread.currentThread().getPriority() > 4) {
+ out.println("Priority attack");
+ }
+ runAttack2();
+
+ try {
+ this.wait();
+ } catch (InterruptedException e) {}
+ }
+ }
+}
diff --git a/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/FileAttack.java b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/FileAttack.java
new file mode 100644
index 0000000..9f76ccf
--- /dev/null
+++ b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/FileAttack.java
@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2001-2016 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 tested.robots;
+
+
+import robocode.AdvancedRobot;
+import robocode.ScannedRobotEvent;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+
+/**
+ * @author Pavel Savara (original)
+ */
+public class FileAttack extends AdvancedRobot {
+
+ @Override
+ public void run() {
+ // noinspection InfiniteLoopStatement
+ for (;;) {
+ turnLeft(100);
+ ahead(10);
+ turnLeft(100);
+ back(10);
+ }
+ }
+
+ @Override
+ public void onScannedRobot(ScannedRobotEvent event) {
+ readAttack();
+ writeAttack();
+ }
+
+ private void readAttack() {
+ try {
+ FileInputStream fs = new FileInputStream("C:\\MSDOS.SYS");
+
+ System.out.print(fs.read());
+ System.out.print(fs.read());
+ System.out.print(fs.read());
+ System.out.print(fs.read());
+ fs.close();
+ } catch (FileNotFoundException e) {
+ e.printStackTrace(out);
+ } catch (IOException e) {
+ e.printStackTrace(out);
+ } catch (RuntimeException e) {
+ // swalow security exception
+ e.printStackTrace(out);
+ }
+ }
+
+ private void writeAttack() {
+ FileOutputStream fs;
+
+ try {
+ fs = new FileOutputStream("C:\\Robocode.attack");
+ fs.write(0xBA);
+ fs.write(0xDF);
+ fs.write(0x00);
+ fs.write(0xD0);
+ fs.close();
+ } catch (FileNotFoundException e) {
+ e.printStackTrace(out);
+ } catch (IOException e) {
+ e.printStackTrace(out);
+ } catch (RuntimeException e) {
+ // swalow security exception
+ e.printStackTrace(out);
+ }
+ }
+
+}
diff --git a/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/IncludeNamespaceAttack.java b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/IncludeNamespaceAttack.java
new file mode 100644
index 0000000..c5ecdb9
--- /dev/null
+++ b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/IncludeNamespaceAttack.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2001-2016 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 tested.robots;
+
+
+import net.sf.robocode.security.HiddenAccess;
+import robocode.AdvancedRobot;
+import robocode.ScannedRobotEvent;
+
+
+/**
+ * @author Pavel Savara (original)
+ */
+public class IncludeNamespaceAttack extends AdvancedRobot {
+
+ @Override
+ public void run() {
+ // noinspection InfiniteLoopStatement
+ for (;;) {
+ turnLeft(100);
+ ahead(10);
+ turnLeft(100);
+ back(10);
+ }
+ }
+
+ @Override
+ public void onScannedRobot(ScannedRobotEvent event) {
+ namespaceAttack();
+ }
+
+ private void namespaceAttack() {
+ try {
+ HiddenAccess.createRules(10, 10, 10, 10, 1, false, 100);
+ } catch (RuntimeException e) {
+ // Swallow security exception
+ e.printStackTrace(out);
+ }
+ }
+}
diff --git a/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ThreadAttack.java b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ThreadAttack.java
new file mode 100644
index 0000000..bb21f8c
--- /dev/null
+++ b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ThreadAttack.java
@@ -0,0 +1,87 @@
+/**
+ * Copyright (c) 2001-2016 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 tested.robots;
+
+
+import robocode.AdvancedRobot;
+import robocode.ScannedRobotEvent;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+
+/**
+ * @author Pavel Savara (original)
+ */
+public class ThreadAttack extends AdvancedRobot {
+
+ @Override
+ public void run() {
+ // noinspection InfiniteLoopStatement
+ for (;;) {
+ turnLeft(100);
+ ahead(10);
+ turnLeft(100);
+ back(10);
+ }
+ }
+
+ @Override
+ public void onScannedRobot(ScannedRobotEvent event) {
+ runAttack();
+ runAttack2();
+ }
+
+ private void runAttack() {
+ try {
+ Attacker a = new Attacker();
+ Thread t = new Thread(a);
+
+ t.start();
+ } catch (RuntimeException e) {
+ // swallow security exception
+ e.printStackTrace(out);
+ }
+ }
+
+ private void runAttack2() {
+ try {
+ Attacker a = new Attacker();
+ ThreadGroup tg = new ThreadGroup("MyAttack");
+
+ tg.setMaxPriority(10);
+ Thread t = new Thread(tg, a);
+
+ t.start();
+ } catch (RuntimeException e) {
+ // swallow security exception
+ e.printStackTrace(out);
+ }
+ }
+
+ private AtomicInteger counter = new AtomicInteger();
+
+ private class Attacker implements Runnable {
+
+ public synchronized void run() {
+ final int id = counter.incrementAndGet();
+
+ out.println("Running id:" + id);
+
+ if (Thread.currentThread().getPriority() > 4) {
+ out.println("Priority attack");
+ }
+ runAttack2();
+
+ try {
+ this.wait();
+ } catch (InterruptedException e) {
+ out.println("Interrupted id:" + id);
+ }
+ }
+ }
+}
diff --git a/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ThreadGroupAttack.java b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ThreadGroupAttack.java
new file mode 100644
index 0000000..963b549
--- /dev/null
+++ b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/ThreadGroupAttack.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2001-2016 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 tested.robots;
+
+
+import robocode.*;
+
+
+/**
+ * This nasty robot tries to interrupt the thread of each opponent robot that it scans.
+ * It enumerates the threads recursively of thread group that is a parent of its own
+ * thread group to find out, which threads that are active. These threads are all robot
+ * threads.
+ *
+ * This robot is inspired by the hacker.Destroyer 1.3, which proved a security breach in
+ * Robocode 1.7.2.1 Beta. The security breach was reported with:
+ * Bug [3021140] Possible for robot to kill other robot threads.
+ *
+ * The security manager of Robocode must make sure that unsafe (robot) threads cannot
+ * access thread groups other than its own thread group within checkAccess(Thread).
+ *
+ * @author Flemming N. Larsen (original)
+ */
+public class ThreadGroupAttack extends Robot {
+ private Thread[] threads = new Thread[100];
+
+ public void run() {
+ runAttack();
+
+ while (true) {
+ turnGunLeft(30);
+ }
+ }
+
+ private void runAttack() {
+ try {
+ new Thread(new Runnable() {
+ public void run() {
+ ThreadGroup parentGroup = Thread.currentThread().getThreadGroup().getParent();
+
+ while (true) {
+ parentGroup.enumerate(threads, true);
+ try {
+ Thread.sleep(0);
+ } catch (InterruptedException ignore) {}
+ }
+ }
+ }).start();
+ } catch (RuntimeException t) {
+ t.printStackTrace(out);
+ }
+ }
+
+ public void onScannedRobot(ScannedRobotEvent e) {
+ attackRobotThread(e.getName());
+ }
+
+ private void attackRobotThread(String robotName) {
+ for (Thread t : threads) {
+ if (t != null && robotName.equals(t.getName())) {
+ t.interrupt();
+ out.println("Interrupted: " + robotName);
+ }
+ }
+ }
+}
diff --git a/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/UndeadThread.java b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/UndeadThread.java
new file mode 100644
index 0000000..75fcbfa
--- /dev/null
+++ b/代码/workspace_robo4/robocode.tests.robots/src/main/java/tested/robots/UndeadThread.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2001-2016 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 tested.robots;
+
+
+import robocode.AdvancedRobot;
+
+
+/**
+ * @author Pavel Savara (original)
+ */
+public class UndeadThread extends AdvancedRobot {
+
+ @Override
+ public void run() {
+ out.println("I will live forever!");
+ // noinspection InfiniteLoopStatement
+ while (true) {
+ try {
+ body();
+ } catch (RuntimeException t) {
+ // spamming the console
+ out.println("Swalowed it, HA HA HA HA HAAAAA !!!!!");
+ out.println(t);
+ }
+ }
+ }
+
+ private void body() {
+ // noinspection InfiniteLoopStatement
+ for (;;) {
+ turnLeft(100);
+ ahead(10);
+ turnLeft(100);
+ back(10);
+ }
+ }
+
+}
diff --git a/代码/workspace_robo4/robocode.tests/src/main/java/net/sf/robocode/test/helpers/Assert.java b/代码/workspace_robo4/robocode.tests/src/main/java/net/sf/robocode/test/helpers/Assert.java
new file mode 100644
index 0000000..27c29dd
--- /dev/null
+++ b/代码/workspace_robo4/robocode.tests/src/main/java/net/sf/robocode/test/helpers/Assert.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2001-2016 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.test.helpers;
+
+
+import robocode.util.Utils;
+
+
+/**
+ * @author Pavel Savara (original)
+ */
+public class Assert extends org.junit.Assert {
+ public static void assertNear(double v1, double v2) {
+ org.junit.Assert.assertEquals(v1, v2, Utils.NEAR_DELTA);
+ }
+
+ public static void allAssertNear(double v1, double v2) {
+ try {
+ assertNear(v1, v2);
+ } catch (RuntimeException ex) {
+ ex.printStackTrace(System.err);
+ }
+ }
+
+ public static void allAssertThat(T t, org.hamcrest.Matcher tMatcher) {
+ try {
+ org.junit.Assert.assertThat(t, tMatcher);
+ } catch (RuntimeException ex) {
+ ex.printStackTrace(System.err);
+ }
+ }
+
+}
diff --git a/代码/workspace_robo4/robocode.ui.editor/src/main/java/net/sf/robocode/ui/editor/EditWindow.java b/代码/workspace_robo4/robocode.ui.editor/src/main/java/net/sf/robocode/ui/editor/EditWindow.java
new file mode 100644
index 0000000..a0f8e9b
--- /dev/null
+++ b/代码/workspace_robo4/robocode.ui.editor/src/main/java/net/sf/robocode/ui/editor/EditWindow.java
@@ -0,0 +1,485 @@
+/**
+ * Copyright (c) 2001-2016 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.ui.editor;
+
+
+import net.sf.robocode.io.FileUtil;
+import net.sf.robocode.io.Logger;
+import net.sf.robocode.repository.IRepositoryManager;
+import net.sf.robocode.ui.editor.theme.EditorThemeProperties;
+import net.sf.robocode.ui.editor.theme.EditorThemePropertiesManager;
+import net.sf.robocode.ui.editor.theme.EditorThemePropertyChangeAdapter;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.event.InternalFrameAdapter;
+import javax.swing.event.InternalFrameEvent;
+import javax.swing.filechooser.FileFilter;
+
+import java.awt.Font;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.StringTokenizer;
+
+
+/**
+ * @author Mathew A. Nelson (original)
+ * @author Flemming N. Larsen (contributor)
+ * @author Matthew Reeder (contributor)
+ */
+@SuppressWarnings("serial")
+public class EditWindow extends JInternalFrame {
+
+ private String fileName;
+ private String robotName;
+ public boolean modified;
+ private final RobocodeEditor editor;
+ private final IRepositoryManager repositoryManager;
+ private final File robotsDirectory;
+ private EditorPanel editorPanel;
+ private EditorPane editorPane;
+
+ public EditWindow(IRepositoryManager repositoryManager, RobocodeEditor editor, File robotsDirectory) {
+ super("Edit Window", true, true, true, true);
+ this.editor = editor;
+ this.robotsDirectory = robotsDirectory;
+ this.repositoryManager = repositoryManager;
+ initialize();
+ }
+
+ public EditorPane getEditorPane() {
+ if (editorPane == null) {
+ editorPane = editorPanel.getEditorPane();
+ InputMap im = editorPane.getInputMap();
+
+ // FIXME: Replace hack with better solution than using 'ctrl H'
+ im.put(KeyStroke.getKeyStroke("ctrl H"), editor.getReplaceAction());
+ }
+ return editorPane;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public String getRobotName() {
+ return robotName;
+ }
+
+ private void initialize() {
+ try {
+ addInternalFrameListener(new InternalFrameAdapter() {
+ @Override
+ public void internalFrameClosing(InternalFrameEvent e) {
+ if (!modified || fileSave(true)) {
+ editor.setLineStatus(-1);
+ dispose();
+ }
+ editor.removeFromWindowMenu(EditWindow.this);
+ }
+
+ @Override
+ public void internalFrameDeactivated(InternalFrameEvent e) {
+ editor.setLineStatus(-1);
+ }
+
+ @Override
+ public void internalFrameIconified(InternalFrameEvent e) {
+ editor.setLineStatus(-1);
+ }
+ });
+ setFrameIcon(new ImageIcon(EditWindow.class.getResource("/net/sf/robocode/ui/icons/robocode-icon.png")));
+ setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+
+ setSize(750, 500);
+
+ editor.addToWindowMenu(this);
+
+ editorPanel = new EditorPanel();
+ setContentPane(editorPanel);
+
+ EditorThemeProperties currentThemeProps = EditorThemePropertiesManager.getCurrentEditorThemeProperties();
+ Font font = currentThemeProps.getFont();
+ editorPanel.setFont(font);
+
+ // Make sure the source editor window gets focus with a blinking cursor
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ editorPanel.getEditorPane().requestFocus();
+ }
+ });
+
+ EditorThemePropertiesManager.addListener(new EditorThemePropertyChangeAdapter() {
+ @Override
+ public void onFontChanged(Font newFont) {
+ editorPanel.setFont(newFont);
+ }
+ });
+
+ final JavaDocument document = (JavaDocument) editorPanel.getEditorPane().getDocument();
+
+ document.addDocumentListener(new DocumentListener() {
+ public void removeUpdate(DocumentEvent e) {
+ updateModificationState();
+ }
+
+ public void insertUpdate(DocumentEvent e) {
+ updateModificationState();
+ }
+
+ public void changedUpdate(DocumentEvent e) {
+ updateModificationState();
+ }
+
+ // Bug-361 Problem in the text editor related with the .java file modification
+ private void updateModificationState() {
+ setModified(editorPanel.getEditorPane().isModified());
+ }
+ });
+ } catch (RuntimeException e) {
+ Logger.logError(e);
+ }
+ }
+
+ public void setFileName(String newFileName) {
+ fileName = newFileName;
+ updateTitle();
+ }
+
+ public void setRobotName(String newRobotName) {
+ robotName = newRobotName;
+ updateTitle();
+ }
+
+ private void updateTitle() {
+ StringBuffer titleBuf = new StringBuffer("Editing");
+ if (fileName != null) {
+ titleBuf.append(" - ").append(fileName);
+ } else if (robotName != null) {
+ titleBuf.append(" - ").append(robotName);
+ }
+ if (modified) {
+ titleBuf.append(" *");
+ }
+ setTitle(titleBuf.toString());
+ }
+
+ private void setModified(boolean modified) {
+ boolean updated = (modified != this.modified);
+ if (updated) {
+ this.modified = modified;
+ updateTitle();
+ editor.setSaveFileMenuItemsEnabled(modified);
+ }
+ }
+
+ public void compile() {
+ if (!fileSave(true, true)) {
+ error("You must save before compiling.");
+ return;
+ }
+ if (editor.getCompiler() != null) {
+ // The compiler + refresh of the repository is done in a thread in order to avoid the compiler
+ // window hanging while compiling. The SwingUtilities.invokeLater() does not do a good job here
+ // (window is still hanging). Hence, a real thread running beside the EDT is used, which does a
+ // great job, where each each new print from the compiler is written out as soon as it is ready
+ // in the output stream.
+ new Thread(new Runnable() {
+ public void run() {
+ if (fileName == null) {
+ error("You must save before compiling.");
+ return;
+ }
+ editor.getCompiler().compile(getRobotDir(), fileName);
+ repositoryManager.refresh(fileName);
+ }
+ }).start();
+ } else {
+ JOptionPane.showMessageDialog(editor, "No compiler installed.", "Error", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ private void error(String msg) {
+ Object[] options = {
+ "OK"
+ };
+
+ JOptionPane.showOptionDialog(this, msg, "Error", JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE, null,
+ options, options[0]);
+ }
+
+ public boolean fileSave(boolean confirm) {
+ return fileSave(confirm, false);
+ }
+
+ private boolean fileSave(boolean confirm, boolean mustSave) {
+ if (confirm) {
+ if (!modified) {
+ return true;
+ }
+ String s = fileName;
+
+ if (s == null) {
+ s = robotName;
+ }
+ if (s == null) {
+ s = "This file";
+ }
+ int ok = JOptionPane.showConfirmDialog(this, s + " has been modified. Do you wish to save it?",
+ "Modified file", JOptionPane.YES_NO_CANCEL_OPTION);
+
+ if (ok == JOptionPane.NO_OPTION) {
+ return !mustSave;
+ }
+ if (ok == JOptionPane.CANCEL_OPTION) {
+ return false;
+ }
+ }
+ String fileName = getFileName();
+
+ if (fileName == null) {
+ return fileSaveAs();
+ }
+
+ String reasonableFilename = getReasonableFilename();
+
+ if (reasonableFilename != null) {
+ try {
+ String a = new File(reasonableFilename).getCanonicalPath();
+ String b = new File(fileName).getCanonicalPath();
+
+ if (!a.equals(b)) {
+ int ok = JOptionPane.showConfirmDialog(this,
+ fileName + " should be saved in: \n" + reasonableFilename
+ + "\n Would you like to save it there instead?",
+ "Name has changed",
+ JOptionPane.YES_NO_CANCEL_OPTION);
+
+ if (ok == JOptionPane.CANCEL_OPTION) {
+ return false;
+ }
+ if (ok == JOptionPane.YES_OPTION) {
+ return fileSaveAs();
+ }
+ }
+ } catch (IOException e) {
+ Logger.logError("Unable to check reasonable filename: ", e);
+ }
+ }
+
+ BufferedWriter bufferedWriter = null;
+ OutputStreamWriter outputStreamWriter = null;
+ FileOutputStream fileOutputStream = null;
+
+ try {
+ fileOutputStream = new FileOutputStream(fileName);
+ outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF8");
+ bufferedWriter = new BufferedWriter(outputStreamWriter);
+
+ getEditorPane().write(bufferedWriter);
+ setModified(false);
+ } catch (IOException e) {
+ error("Cannot write file: " + e);
+ return false;
+ } finally {
+ FileUtil.cleanupStream(bufferedWriter);
+ }
+ return true;
+ }
+
+ private String getRobotDir() {
+ String saveDir = robotsDirectory.getPath() + File.separatorChar;
+
+ String text = getEditorPane().getText();
+ int pIndex = text.indexOf("package ");
+
+ if (pIndex >= 0) {
+ int pEIndex = text.indexOf(";", pIndex);
+
+ if (pEIndex > 0) {
+ String packageTree = text.substring(pIndex + 8, pEIndex) + File.separatorChar;
+
+ packageTree = packageTree.replace('.', File.separatorChar);
+
+ saveDir += packageTree;
+ }
+ }
+ return saveDir;
+ }
+
+ public boolean fileSaveAs() {
+ String javaFileName = null;
+ String saveDir = getRobotDir();
+
+ String text = getEditorPane().getText();
+
+ int pIndex = text.indexOf("public class ");
+
+ if (pIndex >= 0) {
+ int pEIndex = text.indexOf(" ", pIndex + 13);
+
+ if (pEIndex > 0) {
+ int pEIndex2 = text.indexOf("\n", pIndex + 13);
+
+ if (pEIndex2 > 0 && pEIndex2 < pEIndex) {
+ pEIndex = pEIndex2;
+ }
+ javaFileName = text.substring(pIndex + 13, pEIndex).trim() + ".java";
+ } else {
+ pEIndex = text.indexOf("\n", pIndex + 13);
+ if (pEIndex > 0) {
+ javaFileName = text.substring(pIndex + 13, pEIndex).trim() + ".java";
+ }
+ }
+ }
+
+ File f = new File(saveDir);
+
+ if (!f.exists()) {
+ int ok = JOptionPane.showConfirmDialog(this,
+ "Your robot should be saved in the directory: " + saveDir
+ + "\nThis directory does not exist, would you like to create it?",
+ "Create Directory",
+ JOptionPane.YES_NO_CANCEL_OPTION);
+
+ if (ok == JOptionPane.YES_OPTION) {
+ if (!f.exists() && !f.mkdirs()) {
+ Logger.logError("Cannot create: " + f);
+ }
+ f = new File(saveDir);
+ }
+ if (ok == JOptionPane.CANCEL_OPTION) {
+ return false;
+ }
+ }
+
+ JFileChooser chooser;
+
+ chooser = new JFileChooser(f);
+ chooser.setCurrentDirectory(f);
+
+ FileFilter filter = new FileFilter() {
+ @Override
+ public boolean accept(File pathname) {
+ if (pathname.isDirectory()) {
+ return true;
+ }
+ String fn = pathname.getName();
+ int idx = fn.lastIndexOf('.');
+ String extension = "";
+
+ if (idx >= 0) {
+ extension = fn.substring(idx);
+ }
+ return extension.equalsIgnoreCase(".java");
+ }
+
+ @Override
+ public String getDescription() {
+ return "Robots";
+ }
+ };
+
+ chooser.setFileFilter(filter);
+
+ boolean done = false;
+
+ while (!done) {
+ done = true;
+ if (javaFileName != null) {
+ chooser.setSelectedFile(new File(f, javaFileName));
+ }
+ int rv = chooser.showSaveDialog(this);
+ String robotFileName;
+
+ if (rv == JFileChooser.APPROVE_OPTION) {
+ robotFileName = chooser.getSelectedFile().getPath();
+ File outFile = new File(robotFileName);
+
+ if (outFile.exists()) {
+ int ok = JOptionPane.showConfirmDialog(this,
+ robotFileName + " already exists. Are you sure you want to replace it?", "Warning",
+ JOptionPane.YES_NO_CANCEL_OPTION);
+
+ if (ok == JOptionPane.NO_OPTION) {
+ done = false;
+ continue;
+ }
+ if (ok == JOptionPane.CANCEL_OPTION) {
+ return false;
+ }
+ }
+ setFileName(robotFileName);
+ fileSave(false);
+ } else {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public String getPackage() {
+ String text = getEditorPane().getText();
+ int pIndex = text.indexOf("package ");
+
+ if (pIndex >= 0) {
+ int pEIndex = text.indexOf(";", pIndex);
+
+ if (pEIndex > 0) {
+ return text.substring(pIndex + 8, pEIndex);
+ }
+ }
+ return "";
+ }
+
+ private String getReasonableFilename() {
+ StringBuffer fileName = new StringBuffer(robotsDirectory.getPath()).append(File.separatorChar);
+ String javaFileName;
+ String packageTree = null;
+
+ String text = getEditorPane().getText();
+ StringTokenizer tokenizer = new StringTokenizer(text, " \t\r\n;");
+ String token;
+ boolean inComment = false;
+
+ while (tokenizer.hasMoreTokens()) {
+ token = tokenizer.nextToken();
+ if (!inComment && (token.equals("/*") || token.equals("/**"))) {
+ inComment = true;
+ }
+ if (inComment && (token.equals("*/") || token.equals("**/"))) {
+ inComment = false;
+ }
+ if (inComment) {
+ continue;
+ }
+
+ if (packageTree == null && token.equals("package")) {
+ packageTree = tokenizer.nextToken();
+ if (packageTree == null || packageTree.length() == 0) {
+ return null;
+ }
+ packageTree = packageTree.replace('.', File.separatorChar);
+ packageTree += File.separator;
+ fileName.append(packageTree);
+ }
+ if (token.equals("class")) {
+ javaFileName = tokenizer.nextToken() + ".java";
+ fileName.append(javaFileName);
+ return fileName.toString();
+ }
+ }
+ return null;
+ }
+}
diff --git a/代码/workspace_robo4/robocode.ui.editor/src/main/java/net/sf/robocode/ui/editor/WindowMenuItem.java b/代码/workspace_robo4/robocode.ui.editor/src/main/java/net/sf/robocode/ui/editor/WindowMenuItem.java
new file mode 100644
index 0000000..bda1627
--- /dev/null
+++ b/代码/workspace_robo4/robocode.ui.editor/src/main/java/net/sf/robocode/ui/editor/WindowMenuItem.java
@@ -0,0 +1,291 @@
+/**
+ * Copyright (c) 2001-2016 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.ui.editor;
+
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.io.File;
+
+
+/**
+ * Customized JMenuItem where each item is bound to a specific JInternalFrame,
+ * so we can have a dynamic menu of open windows.
+ *
+ * @author Matthew Reeder (original)
+ */
+@SuppressWarnings("serial")
+public class WindowMenuItem extends JCheckBoxMenuItem implements ActionListener {
+
+ // Maximum number of windows that will be shown on the menu (to get the rest, you'll
+ // have to open the dialog). The number 9 is also the number of most recently used
+ // files that normally show up in other applications. The reason is so that you can
+ // give them dynamic hotkeys from 1 to 9. Otherwise, there's no reason (besides
+ // avoiding taking up way too much space) to limit the size of the menu.
+ public static final int WINDOW_MENU_MAX_SIZE = 9;
+ // Number of static menu items before the dynamic menu (including seperators)
+ public static final int PRECEDING_WINDOW_MENU_ITEMS = 3;
+ // Number of static menu items after the dynamic menu (including seperators
+ // and the More Windows... menu item)
+ public static final int SUBSEQUENT_WINDOW_MENU_ITEMS = 1;
+ // Normal max length of a window name
+ public static final int MAX_WINDOW_NAME_LENGTH = 30;
+ // I make one "special" menu item that isn't tied to a window. Since it has
+ // similar needs for enabling/visibility and labeling, I made it the same class.
+ public static final int REGULAR_WINDOW = 0, SPECIAL_MORE = 2;
+ private EditWindow window;
+ private JMenu parentMenu;
+ private final int type;
+
+ public WindowMenuItem(EditWindow window, JMenu parentMenu) {
+ super();
+ this.window = window;
+ this.parentMenu = parentMenu;
+ type = REGULAR_WINDOW;
+ parentMenu.add(this, parentMenu.getMenuComponentCount() - SUBSEQUENT_WINDOW_MENU_ITEMS);
+ addActionListener(this);
+ }
+
+ /**
+ * WindowMenuItem Constructor for "More Windows..." menu.
+ */
+ public WindowMenuItem() {
+ type = SPECIAL_MORE;
+ }
+
+ /**
+ * Event handler for the menu item
+ *
+ * Brings the window to the front. This should be called for the "More
+ * Windows..." Item, because it doesn't make itself its own ActionListener.
+ *
+ * Note that e can be null, and this menu item might not be showing (if this
+ * is called from the "More Windows" dialog).
+ */
+ public void actionPerformed(ActionEvent e) {
+ if (window.isIcon()) {
+ try {
+ window.setIcon(false);
+ } catch (RuntimeException ignored) {}
+ }
+ if (window.getDesktopPane() != null) {
+ window.getDesktopPane().setSelectedFrame(window);
+ }
+ window.toFront();
+ window.grabFocus();
+ try {
+ window.setSelected(true);
+ } catch (RuntimeException ignored) {}
+ }
+
+ /**
+ * Returns the label that should be used. If the menu item is supposed to be
+ * hidden, this may not be a real valid label.
+ */
+ @Override
+ public String getText() {
+ if (type == SPECIAL_MORE) {
+ Container parent = getParent();
+
+ if (parent == null) {
+ return "";
+ }
+
+ int numWindows = parent.getComponentCount() - PRECEDING_WINDOW_MENU_ITEMS - SUBSEQUENT_WINDOW_MENU_ITEMS;
+
+ if (numWindows <= 0) {
+ return "No Windows Open";
+ }
+
+ return "More Windows...";
+ }
+ if (window == null || parentMenu == null) {
+ return "";
+ }
+ String text = (getIndex() + 1) + " " + getFileName();
+
+ if (window.modified) {
+ text += " *";
+ }
+ return text;
+ }
+
+ protected String getFileName() {
+ if (window.getFileName() == null) {
+ return "Untitled " + (getPrecedingNewFiles() + 1);
+ }
+
+ String name = window.getFileName();
+
+ if (name.length() < MAX_WINDOW_NAME_LENGTH) {
+ return name;
+ }
+ if (name.indexOf(File.separatorChar) < 0) {
+ return name;
+ } // If there are no separators, I can't really intelligently truncate.
+ int startLength = name.indexOf(File.separatorChar, 1) + 1;
+ int endLength = name.length() - name.lastIndexOf(File.separatorChar);
+
+ if (endLength + startLength + 3 > name.length()) {
+ return name;
+ } // return name anyways, since we're not getting it any shorter.
+
+ boolean change;
+
+ do {
+ change = false;
+ int newEndLength = name.length() - name.lastIndexOf(File.separatorChar, name.length() - endLength - 1);
+
+ if (newEndLength + startLength + 3 <= MAX_WINDOW_NAME_LENGTH) {
+ endLength = newEndLength;
+ change = true;
+ }
+ int newStartLength = name.indexOf(File.separatorChar, startLength + 1) + 1;
+
+ if (endLength + startLength + 3 <= MAX_WINDOW_NAME_LENGTH) {
+ startLength = newStartLength;
+ change = true;
+ }
+ } while (change);
+
+ return name.substring(0, startLength) + "..." + name.substring(name.length() - endLength);
+ }
+
+ /**
+ * @return how many nameless windows occur before this one in the parent.
+ */
+ protected int getPrecedingNewFiles() {
+ int count = 0;
+
+ for (int i = 0; i < WINDOW_MENU_MAX_SIZE
+ && i < parentMenu.getMenuComponentCount() - PRECEDING_WINDOW_MENU_ITEMS - SUBSEQUENT_WINDOW_MENU_ITEMS
+ && parentMenu.getMenuComponent(i + PRECEDING_WINDOW_MENU_ITEMS) != this; i++) {
+ if (parentMenu.getMenuComponent(i + PRECEDING_WINDOW_MENU_ITEMS) instanceof WindowMenuItem
+ && ((WindowMenuItem) parentMenu.getMenuComponent(i + PRECEDING_WINDOW_MENU_ITEMS)).window.getFileName()
+ == null) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Figures out what index (from 0 to WINDOW_MENU_MAX_SIZE-1) this item is in
+ * the window menu.
+ *
+ * @return -1 if this item isn't showing.
+ */
+ protected int getIndex() {
+ for (int i = 0; i < WINDOW_MENU_MAX_SIZE
+ && i < parentMenu.getMenuComponentCount() - PRECEDING_WINDOW_MENU_ITEMS - SUBSEQUENT_WINDOW_MENU_ITEMS; i++) {
+ if (this == parentMenu.getMenuComponent(i + PRECEDING_WINDOW_MENU_ITEMS)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the index of the character in the label that should be underlined
+ */
+ @Override
+ public int getDisplayedMnemonicIndex() {
+ return (type == SPECIAL_MORE) ? 11 : 0;
+ }
+
+ /**
+ * Returns the keyboard mnemonic for this item, which is the virtual key
+ * code for its 1-based index.
+ */
+ @Override
+ public int getMnemonic() {
+ return (type == SPECIAL_MORE) ? KeyEvent.VK_S : KeyEvent.VK_1 + getIndex();
+ }
+
+ /**
+ * Returns true if this item should be showing.
+ *
+ * Returns false if there are more than WINDOW_MENU_MAX_SIZE items before it
+ * in the menu.
+ */
+ @Override
+ public boolean isVisible() {
+ if (type == SPECIAL_MORE) {
+ Container parent = getParent();
+
+ if (parent == null) {
+ return true;
+ }
+ int numWindows = parent.getComponentCount() - PRECEDING_WINDOW_MENU_ITEMS - SUBSEQUENT_WINDOW_MENU_ITEMS;
+
+ updateSelection();
+ return (numWindows <= 0) || (numWindows > WINDOW_MENU_MAX_SIZE);
+ }
+ updateSelection();
+ return getIndex() >= 0;
+ }
+
+ /**
+ * Returns true if this item should be enabled (selectable).
+ *
+ * Returns false if it is a More Windows... item and there are no windows.
+ */
+ @Override
+ public boolean isEnabled() {
+ if (type == SPECIAL_MORE) {
+ Container parent = getParent();
+
+ if (parent == null) {
+ return true;
+ }
+ int numWindows = parent.getComponentCount() - PRECEDING_WINDOW_MENU_ITEMS - SUBSEQUENT_WINDOW_MENU_ITEMS;
+
+ return (numWindows > 0);
+ }
+ return true;
+ }
+
+ /**
+ * Determines if this menu item should currently show as "selected".
+ *
+ * The item should be seleced if the window it's tied to has focus.
+ */
+ @Override
+ public boolean isSelected() {
+ return (type != SPECIAL_MORE) && (window != null && window.getDesktopPane() != null)
+ && window.getDesktopPane().getSelectedFrame() == window;
+ }
+
+ /**
+ * Makes sure the underlying menu item knows if we're selected.
+ */
+ public void updateSelection() {
+ setSelected(isSelected()); // Sort of a silly thing to do...
+ setEnabled(isEnabled());
+ }
+
+ /**
+ * @return the EditWindow that this menu item is tied to.
+ */
+ public EditWindow getEditWindow() {
+ return window;
+ }
+
+ /**
+ * Creates a string representation of this object.
+ *
+ * Handy for repurposing the menu items as list items :-)
+ */
+ @Override
+ public String toString() {
+ return (type == SPECIAL_MORE) ? "" : getFileName();
+ }
+}
diff --git a/代码/workspace_robo4/robocode.ui/src/main/java/net/sf/robocode/ui/WindowManager.java b/代码/workspace_robo4/robocode.ui/src/main/java/net/sf/robocode/ui/WindowManager.java
new file mode 100644
index 0000000..bbdae95
--- /dev/null
+++ b/代码/workspace_robo4/robocode.ui/src/main/java/net/sf/robocode/ui/WindowManager.java
@@ -0,0 +1,632 @@
+/**
+ * Copyright (c) 2001-2016 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.ui;
+
+
+import net.sf.robocode.battle.BattleProperties;
+import net.sf.robocode.battle.BattleResultsTableModel;
+import net.sf.robocode.battle.IBattleManager;
+import net.sf.robocode.core.Container;
+import net.sf.robocode.host.ICpuManager;
+import net.sf.robocode.io.FileUtil;
+import net.sf.robocode.repository.IRepositoryManager;
+import net.sf.robocode.settings.ISettingsManager;
+import net.sf.robocode.ui.battle.AwtBattleAdaptor;
+import net.sf.robocode.ui.dialog.*;
+import net.sf.robocode.ui.packager.RobotPackager;
+import net.sf.robocode.ui.editor.IRobocodeEditor;
+import net.sf.robocode.version.IVersionManager;
+import robocode.control.events.BattleCompletedEvent;
+import robocode.control.events.IBattleListener;
+import robocode.control.snapshot.ITurnSnapshot;
+
+import javax.swing.*;
+import javax.swing.filechooser.FileFilter;
+import java.awt.*;
+import java.io.File;
+import java.io.IOException;
+import java.util.Locale;
+
+
+/**
+ * @author Mathew A. Nelson (original)
+ * @author Flemming N. Larsen (contributor)
+ * @author Luis Crespo (contributor)
+ */
+public class WindowManager implements IWindowManagerExt {
+
+ private final static int TIMER_TICKS_PER_SECOND = 50;
+ private final AwtBattleAdaptor awtAdaptor;
+ private RobotPackager robotPackager;
+ private RobotExtractor robotExtractor;
+ private final ISettingsManager settingsManager;
+ private final IBattleManager battleManager;
+ private final ICpuManager cpuManager;
+ private final IRepositoryManager repositoryManager;
+ private final IVersionManager versionManager;
+ private final IImageManager imageManager;
+ private IRobotDialogManager robotDialogManager;
+ private RobocodeFrame robocodeFrame;
+
+ private boolean isGUIEnabled = true;
+ private boolean isSlave;
+ private boolean centerRankings = true;
+ private boolean oldRankingHideState = true;
+ private boolean showResults = true;
+
+ public WindowManager(ISettingsManager settingsManager, IBattleManager battleManager, ICpuManager cpuManager, IRepositoryManager repositoryManager, IImageManager imageManager, IVersionManager versionManager) {
+ this.settingsManager = settingsManager;
+ this.battleManager = battleManager;
+ this.repositoryManager = repositoryManager;
+ this.cpuManager = cpuManager;
+ this.versionManager = versionManager;
+ this.imageManager = imageManager;
+ awtAdaptor = new AwtBattleAdaptor(battleManager, TIMER_TICKS_PER_SECOND, true);
+
+ // we will set UI better priority than robots and battle have
+ EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ try {
+ Thread.currentThread().setPriority(Thread.NORM_PRIORITY + 2);
+ } catch (SecurityException ex) {// that's a pity
+ }
+ }
+ });
+ }
+
+ public void setBusyPointer(boolean enabled) {
+ robocodeFrame.setBusyPointer(enabled);
+ }
+
+ public synchronized void addBattleListener(IBattleListener listener) {
+ awtAdaptor.addListener(listener);
+ }
+
+ public synchronized void removeBattleListener(IBattleListener listener) {
+ awtAdaptor.removeListener(listener);
+ }
+
+ public boolean isGUIEnabled() {
+ return isGUIEnabled;
+ }
+
+ public void setEnableGUI(boolean enable) {
+ isGUIEnabled = enable;
+
+ // Set the system property so the AWT headless mode.
+ // Read more about headless mode here:
+ // http://java.sun.com/developer/technicalArticles/J2SE/Desktop/headless/
+ System.setProperty("java.awt.headless", "" + !enable);
+ }
+
+ public void setSlave(boolean value) {
+ isSlave = value;
+ }
+
+ public boolean isSlave() {
+ return isSlave;
+ }
+
+ public boolean isIconified() {
+ return robocodeFrame.isIconified();
+ }
+
+ public boolean isShowResultsEnabled() {
+ return settingsManager.getOptionsCommonShowResults() && showResults;
+ }
+
+ public void setEnableShowResults(boolean enable) {
+ showResults = enable;
+ }
+
+ public ITurnSnapshot getLastSnapshot() {
+ return awtAdaptor.getLastSnapshot();
+ }
+
+ public int getFPS() {
+ return isIconified() ? 0 : awtAdaptor.getFPS();
+ }
+
+ public RobocodeFrame getRobocodeFrame() {
+ if (robocodeFrame == null) {
+ this.robocodeFrame = Container.getComponent(RobocodeFrame.class);
+ }
+ return robocodeFrame;
+ }
+
+ public void showRobocodeFrame(boolean visible, boolean iconified) {
+ RobocodeFrame frame = getRobocodeFrame();
+
+ if (iconified) {
+ frame.setState(Frame.ICONIFIED);
+ }
+
+ if (visible) {
+ // Pack frame to size all components
+ WindowUtil.packCenterShow(frame);
+
+ WindowUtil.setStatusLabel(frame.getStatusLabel());
+
+ frame.checkUpdateOnStart();
+
+ } else {
+ frame.setVisible(false);
+ }
+ }
+
+ public void showAboutBox() {
+ packCenterShow(Container.getComponent(AboutBox.class), true);
+ }
+
+ public String showBattleOpenDialog(final String defExt, final String name) {
+ JFileChooser chooser = new JFileChooser(battleManager.getBattlePath());
+
+ chooser.setFileFilter(
+ new FileFilter() {
+ @Override
+ public boolean accept(File pathname) {
+ return pathname.isDirectory()
+ || pathname.getName().toLowerCase().lastIndexOf(defExt.toLowerCase())
+ == pathname.getName().length() - defExt.length();
+ }
+
+ @Override
+ public String getDescription() {
+ return name;
+ }
+ });
+
+ if (chooser.showOpenDialog(getRobocodeFrame()) == JFileChooser.APPROVE_OPTION) {
+ return chooser.getSelectedFile().getPath();
+ }
+ return null;
+ }
+
+ public String saveBattleDialog(String path, final String defExt, final String name) {
+ File f = new File(path);
+
+ JFileChooser chooser;
+
+ chooser = new JFileChooser(f);
+
+ javax.swing.filechooser.FileFilter filter = new javax.swing.filechooser.FileFilter() {
+ @Override
+ public boolean accept(File pathname) {
+ return pathname.isDirectory()
+ || pathname.getName().toLowerCase().lastIndexOf(defExt.toLowerCase())
+ == pathname.getName().length() - defExt.length();
+ }
+
+ @Override
+ public String getDescription() {
+ return name;
+ }
+ };
+
+ chooser.setFileFilter(filter);
+ int rv = chooser.showSaveDialog(getRobocodeFrame());
+ String result = null;
+
+ if (rv == JFileChooser.APPROVE_OPTION) {
+ result = chooser.getSelectedFile().getPath();
+ int idx = result.lastIndexOf('.');
+ String extension = "";
+
+ if (idx > 0) {
+ extension = result.substring(idx);
+ }
+ if (!(extension.equalsIgnoreCase(defExt))) {
+ result += defExt;
+ }
+ }
+ return result;
+ }
+
+ public void showVersionsTxt() {
+ showInBrowser("file://" + new File(FileUtil.getCwd(), "").getAbsoluteFile() + File.separator + "versions.md");
+ }
+
+ public void showHelpApi() {
+ showInBrowser(
+ "file://" + new File(FileUtil.getCwd(), "").getAbsoluteFile() + File.separator + "javadoc" + File.separator
+ + "index.html");
+ }
+
+ public void showReadMe() {
+ showInBrowser("file://" + new File(FileUtil.getCwd(), "ReadMe.html").getAbsoluteFile());
+ }
+
+ public void showFaq() {
+ showInBrowser("http://robowiki.net/w/index.php?title=Robocode/FAQ");
+ }
+
+ public void showOnlineHelp() {
+ showInBrowser("http://robowiki.net/w/index.php?title=Robocode/Getting_Started");
+ }
+
+ public void showJavaDocumentation() {
+ showInBrowser("http://docs.oracle.com/javase/6/docs/api/");
+ }
+
+ public void showRobocodeHome() {
+ showInBrowser("http://robocode.sourceforge.net");
+ }
+
+ public void showRoboWiki() {
+ showInBrowser("http://robowiki.net");
+ }
+
+ public void showGoogleGroupRobocode() {
+ showInBrowser("https://groups.google.com/forum/?fromgroups#!forum/robocode");
+ }
+
+ public void showRobocodeRepository() {
+ showInBrowser("http://robocoderepository.com");
+ }
+
+ public void showOptionsPreferences() {
+ try {
+ battleManager.pauseBattle();
+
+ WindowUtil.packCenterShow(getRobocodeFrame(), Container.getComponent(PreferencesDialog.class));
+ } finally {
+ battleManager.resumeIfPausedBattle(); // THIS is just dirty hack-fix of more complex problem with desiredTPS and pausing. resumeBattle() belongs here.
+ }
+ }
+
+ public void showResultsDialog(BattleCompletedEvent event) {
+ final ResultsDialog dialog = Container.getComponent(ResultsDialog.class);
+
+ dialog.setup(event.getSortedResults(), event.getBattleRules().getNumRounds());
+ packCenterShow(dialog, true);
+ }
+
+ public void showRankingDialog(boolean visible) {
+ boolean currentRankingHideState = settingsManager.getOptionsCommonDontHideRankings();
+
+ // Check if the Ranking hide states has changed
+ if (currentRankingHideState != oldRankingHideState) {
+ // Remove current visible RankingDialog, if it is there
+ Container.getComponent(RankingDialog.class).dispose();
+
+ // Replace old RankingDialog, as the owner window must be replaced from the constructor
+ Container.cache.removeComponent(RankingDialog.class);
+ Container.cache.addComponent(RankingDialog.class);
+
+ // Reset flag for centering the dialog the first time it is shown
+ centerRankings = true;
+ }
+
+ RankingDialog rankingDialog = Container.getComponent(RankingDialog.class);
+
+ if (visible) {
+ packCenterShow(rankingDialog, centerRankings);
+ centerRankings = false; // only center the first time Rankings are shown
+ } else {
+ rankingDialog.dispose();
+ }
+
+ // Save current Ranking hide state
+ oldRankingHideState = currentRankingHideState;
+ }
+
+ public void showRobocodeEditor() {
+ JFrame editor = (JFrame) net.sf.robocode.core.Container.getComponent(IRobocodeEditor.class);
+
+ if (!editor.isVisible()) {
+ WindowUtil.packCenterShow(editor);
+ } else {
+ editor.setVisible(true);
+ }
+ }
+
+ public void showRobotPackager() {
+ if (robotPackager != null) {
+ robotPackager.dispose();
+ robotPackager = null;
+ }
+
+ robotPackager = net.sf.robocode.core.Container.factory.getComponent(RobotPackager.class);
+ WindowUtil.packCenterShow(robotPackager);
+ }
+
+ public void showRobotExtractor(JFrame owner) {
+ if (robotExtractor != null) {
+ robotExtractor.dispose();
+ robotExtractor = null;
+ }
+
+ robotExtractor = new net.sf.robocode.ui.dialog.RobotExtractor(owner, this, repositoryManager);
+ WindowUtil.packCenterShow(robotExtractor);
+ }
+
+ public void showSplashScreen() {
+ RcSplashScreen splashScreen = Container.getComponent(RcSplashScreen.class);
+
+ packCenterShow(splashScreen, true);
+
+ WindowUtil.setStatusLabel(splashScreen.getSplashLabel());
+
+ repositoryManager.reload(versionManager.isLastRunVersionChanged());
+
+ WindowUtil.setStatusLabel(splashScreen.getSplashLabel());
+ cpuManager.getCpuConstant();
+
+ WindowUtil.setStatus("");
+ WindowUtil.setStatusLabel(null);
+
+ splashScreen.dispose();
+ }
+
+ public void showNewBattleDialog(BattleProperties battleProperties) {
+ try {
+ battleManager.pauseBattle();
+ final NewBattleDialog battleDialog = Container.createComponent(NewBattleDialog.class);
+
+ battleDialog.setup(settingsManager, battleProperties);
+ WindowUtil.packCenterShow(getRobocodeFrame(), battleDialog);
+ } finally {
+ battleManager.resumeBattle();
+ }
+ }
+
+ public boolean closeRobocodeEditor() {
+ IRobocodeEditor editor = net.sf.robocode.core.Container.getComponent(IRobocodeEditor.class);
+
+ return editor == null || !((JFrame) editor).isVisible() || editor.close();
+ }
+
+ public void showCreateTeamDialog() {
+ TeamCreator teamCreator = Container.getComponent(TeamCreator.class);
+
+ WindowUtil.packCenterShow(teamCreator);
+ }
+
+ public void showImportRobotDialog() {
+ JFileChooser chooser = new JFileChooser();
+
+ chooser.setFileFilter(new FileFilter() {
+ @Override
+ public boolean accept(File pathname) {
+ if (pathname.isHidden()) {
+ return false;
+ }
+ if (pathname.isDirectory()) {
+ return true;
+ }
+ String filename = pathname.getName();
+
+ if (filename.equals("robocode.jar")) {
+ return false;
+ }
+ int idx = filename.lastIndexOf('.');
+
+ String extension = "";
+
+ if (idx >= 0) {
+ extension = filename.substring(idx);
+ }
+ return extension.equalsIgnoreCase(".jar") || extension.equalsIgnoreCase(".zip");
+ }
+
+ @Override
+ public String getDescription() {
+ return "Jar Files";
+ }
+ });
+
+ chooser.setDialogTitle("Select the robot .jar file to copy to " + repositoryManager.getRobotsDirectory());
+
+ if (chooser.showDialog(getRobocodeFrame(), "Import") == JFileChooser.APPROVE_OPTION) {
+ File inputFile = chooser.getSelectedFile();
+ String fileName = inputFile.getName();
+ String extension = "";
+
+ int idx = fileName.lastIndexOf('.');
+
+ if (idx >= 0) {
+ extension = fileName.substring(idx);
+ }
+ if (!extension.equalsIgnoreCase(".jar")) {
+ fileName += ".jar";
+ }
+ File outputFile = new File(repositoryManager.getRobotsDirectory(), fileName);
+
+ if (inputFile.equals(outputFile)) {
+ JOptionPane.showMessageDialog(getRobocodeFrame(),
+ outputFile.getName() + " is already in the robots directory!");
+ return;
+ }
+ if (outputFile.exists()) {
+ if (JOptionPane.showConfirmDialog(getRobocodeFrame(), outputFile + " already exists. Overwrite?",
+ "Warning", JOptionPane.YES_NO_OPTION)
+ == JOptionPane.NO_OPTION) {
+ return;
+ }
+ }
+ if (JOptionPane.showConfirmDialog(getRobocodeFrame(),
+ "Robocode will now copy " + inputFile.getName() + " to " + outputFile.getParent(), "Import robot",
+ JOptionPane.OK_CANCEL_OPTION)
+ == JOptionPane.OK_OPTION) {
+ try {
+ FileUtil.copy(inputFile, outputFile);
+ repositoryManager.refresh();
+ JOptionPane.showMessageDialog(getRobocodeFrame(), "Robot imported successfully.");
+ } catch (IOException e) {
+ JOptionPane.showMessageDialog(getRobocodeFrame(), "Import failed: " + e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Shows a web page using the browser manager.
+ *
+ * @param url The URL of the web page
+ */
+ private void showInBrowser(String url) {
+ try {
+ BrowserManager.openURL(url);
+ } catch (IOException e) {
+ JOptionPane.showMessageDialog(getRobocodeFrame(), e.getMessage(), "Unable to open browser!",
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ public void showSaveResultsDialog(BattleResultsTableModel tableModel) {
+ JFileChooser chooser = new JFileChooser();
+
+ chooser.setFileFilter(new FileFilter() {
+
+ @Override
+ public boolean accept(File pathname) {
+ if (pathname.isHidden()) {
+ return false;
+ }
+ if (pathname.isDirectory()) {
+ return true;
+ }
+ String filename = pathname.getName();
+ int idx = filename.lastIndexOf('.');
+
+ String extension = "";
+
+ if (idx >= 0) {
+ extension = filename.substring(idx);
+ }
+ return extension.equalsIgnoreCase(".csv");
+ }
+
+ @Override
+ public String getDescription() {
+ return "Comma Separated Value (CSV) File Format";
+ }
+ });
+
+ chooser.setDialogTitle("Save battle results");
+
+ if (chooser.showSaveDialog(getRobocodeFrame()) == JFileChooser.APPROVE_OPTION) {
+
+ String filename = chooser.getSelectedFile().getPath();
+
+ if (!filename.endsWith(".csv")) {
+ filename += ".csv";
+ }
+
+ boolean append = settingsManager.getOptionsCommonAppendWhenSavingResults();
+
+ tableModel.saveToFile(filename, append);
+ }
+ }
+
+ /**
+ * Packs, centers, and shows the specified window on the screen.
+ * @param window the window to pack, center, and show
+ * @param center {@code true} if the window must be centered; {@code false} otherwise
+ */
+ private void packCenterShow(Window window, boolean center) {
+ Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+
+ window.pack();
+ if (center) {
+ window.setLocation((screenSize.width - window.getWidth()) / 2, (screenSize.height - window.getHeight()) / 2);
+ }
+ window.setVisible(true);
+ }
+
+ public void cleanup() {
+ if (isGUIEnabled()) {
+ getRobocodeFrame().dispose();
+ }
+ }
+
+ public void setStatus(String s) {
+ WindowUtil.setStatus(s);
+ }
+
+ public void messageWarning(String s) {
+ WindowUtil.messageWarning(s);
+ }
+
+ public IRobotDialogManager getRobotDialogManager() {
+ if (robotDialogManager == null) {
+ robotDialogManager = new RobotDialogManager();
+ }
+ return robotDialogManager;
+ }
+
+ public void init() {
+ setLookAndFeel();
+ imageManager.initialize(); // Make sure this one is initialized so all images are available
+ awtAdaptor.subscribe(isGUIEnabled);
+ }
+
+ /**
+ * Sets the Look and Feel (LAF). This method first try to set the LAF to the
+ * system's LAF. If this fails, it try to use the cross platform LAF.
+ * If this also fails, the LAF will not be changed.
+ */
+ private void setLookAndFeel() {
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (RuntimeException t) {
+ // Work-around for problems with setting Look and Feel described here:
+ // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6468089
+ Locale.setDefault(Locale.US);
+
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (RuntimeException t2) {
+ // For some reason Ubuntu 7 can cause a NullPointerException when trying to getting the LAF
+ System.err.println("Could not set the Look and Feel (LAF). The default LAF is used instead");
+ }
+ }
+ // Java 1.6 provide system specific anti-aliasing. Enable it, if it has not been set
+ if (new Double(System.getProperty("java.specification.version")) >= 1.6) {
+ String aaFontSettings = System.getProperty("awt.useSystemAAFontSettings");
+
+ if (aaFontSettings == null) {
+ System.setProperty("awt.useSystemAAFontSettings", "on");
+ }
+ }
+ }
+
+ public void runIntroBattle() {
+ final File intro = new File(FileUtil.getCwd(), "battles/intro.battle");
+ if (intro.exists()) {
+ battleManager.setBattleFilename(intro.getPath());
+ battleManager.loadBattleProperties();
+
+ final boolean origShowResults = showResults; // save flag for showing the results
+
+ showResults = false;
+ try {
+ battleManager.startNewBattle(battleManager.loadBattleProperties(), true, false);
+ battleManager.setDefaultBattleProperties();
+ robocodeFrame.afterIntroBattle();
+ } finally {
+ showResults = origShowResults; // always restore the original flag for showing the results
+ }
+ }
+ }
+
+ public void setVisibleForRobotEngine(boolean visible) {
+ if (visible && !isGUIEnabled()) {
+ // The GUI must be enabled in order to show the window
+ setEnableGUI(true);
+
+ // Set the Look and Feel (LAF)
+ init();
+ }
+
+ if (isGUIEnabled()) {
+ showRobocodeFrame(visible, false);
+ showResults = visible;
+ }
+ }
+}
diff --git a/代码/workspace_robo4/robocode.ui/src/main/java/net/sf/robocode/ui/battle/AwtBattleAdaptor.java b/代码/workspace_robo4/robocode.ui/src/main/java/net/sf/robocode/ui/battle/AwtBattleAdaptor.java
new file mode 100644
index 0000000..11436ae
--- /dev/null
+++ b/代码/workspace_robo4/robocode.ui/src/main/java/net/sf/robocode/ui/battle/AwtBattleAdaptor.java
@@ -0,0 +1,335 @@
+/**
+ * Copyright (c) 2001-2016 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.ui.battle;
+
+
+import net.sf.robocode.battle.IBattleManager;
+import net.sf.robocode.battle.events.BattleEventDispatcher;
+import net.sf.robocode.battle.snapshot.RobotSnapshot;
+import net.sf.robocode.io.Logger;
+import robocode.control.events.*;
+import robocode.control.snapshot.IRobotSnapshot;
+import robocode.control.snapshot.ITurnSnapshot;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.atomic.AtomicInteger;
+
+
+/**
+ * @author Pavel Savara (original)
+ */
+public final class AwtBattleAdaptor {
+ private boolean isEnabled;
+ private final IBattleManager battleManager;
+ private final BattleEventDispatcher battleEventDispatcher = new BattleEventDispatcher();
+ private final BattleObserver observer;
+ private final Timer timerTask;
+
+ private final AtomicReference snapshot;
+ private final AtomicBoolean isRunning;
+ private final AtomicBoolean isPaused;
+ private final AtomicInteger majorEvent;
+ private final AtomicInteger lastMajorEvent;
+ private ITurnSnapshot lastSnapshot;
+ private StringBuilder[] outCache;
+
+ public AwtBattleAdaptor(IBattleManager battleManager, int maxFps, boolean skipSameFrames) {
+ this.battleManager = battleManager;
+ snapshot = new AtomicReference(null);
+
+ this.skipSameFrames = skipSameFrames;
+ timerTask = new Timer(1000 / maxFps, new TimerTask());
+ isRunning = new AtomicBoolean(false);
+ isPaused = new AtomicBoolean(false);
+ majorEvent = new AtomicInteger(0);
+ lastMajorEvent = new AtomicInteger(0);
+
+ observer = new BattleObserver();
+ }
+
+ protected void finalize() throws Throwable {
+ try {
+ timerTask.stop();
+ battleManager.removeListener(observer);
+ } finally {
+ super.finalize();
+ }
+ }
+
+ public void subscribe(boolean isEnabled) {
+ if (this.isEnabled && !isEnabled) {
+ battleManager.removeListener(observer);
+ timerTask.stop();
+ isEnabled = false;
+ } else if (!this.isEnabled && isEnabled) {
+ battleManager.addListener(observer);
+ isEnabled = true;
+ }
+ }
+
+ public synchronized void addListener(IBattleListener listener) {
+ battleEventDispatcher.addListener(listener);
+ }
+
+ public synchronized void removeListener(IBattleListener listener) {
+ battleEventDispatcher.removeListener(listener);
+ }
+
+ public ITurnSnapshot getLastSnapshot() {
+ return lastSnapshot;
+ }
+
+ // this is always dispatched on AWT thread
+ private void awtOnTurnEnded(boolean forceRepaint, boolean readoutText) {
+ try {
+ ITurnSnapshot current = snapshot.get();
+
+ if (current == null) { // !isRunning.get() ||
+ // paint logo
+ lastSnapshot = null;
+ battleEventDispatcher.onTurnEnded(new TurnEndedEvent(null));
+ } else {
+ if (lastSnapshot != current || !skipSameFrames || forceRepaint) {
+ lastSnapshot = current;
+
+ IRobotSnapshot[] robots = null;
+
+ if (readoutText) {
+ synchronized (snapshot) {
+ robots = lastSnapshot.getRobots();
+
+ for (int i = 0; i < robots.length; i++) {
+ RobotSnapshot robot = (RobotSnapshot) robots[i];
+
+ final StringBuilder cache = outCache[i];
+
+ if (cache.length() > 0) {
+ robot.setOutputStreamSnapshot(cache.toString());
+ outCache[i].setLength(0);
+ }
+ }
+ }
+ }
+
+ battleEventDispatcher.onTurnEnded(new TurnEndedEvent(lastSnapshot));
+
+ if (readoutText) {
+ for (IRobotSnapshot robot : robots) {
+ ((RobotSnapshot) robot).setOutputStreamSnapshot(null);
+ }
+ }
+
+ calculateFPS();
+ }
+ }
+ } catch (RuntimeException t) {
+ Logger.logError(t);
+ }
+ }
+
+ public int getFPS() {
+ return fps;
+ }
+
+ // FPS (frames per second) calculation
+ private int fps;
+ private long measuredFrameCounter;
+ private long measuredFrameStartTime;
+ private final boolean skipSameFrames;
+
+ private void calculateFPS() {
+ // Calculate the current frames per second (FPS)
+
+ if (measuredFrameCounter++ == 0) {
+ measuredFrameStartTime = System.nanoTime();
+ }
+
+ long deltaTime = System.nanoTime() - measuredFrameStartTime;
+
+ if (deltaTime / 1000000000 >= 1) {
+ fps = (int) (measuredFrameCounter * 1000000000L / deltaTime);
+ measuredFrameCounter = 0;
+ }
+ }
+
+ private class TimerTask implements ActionListener {
+ public void actionPerformed(ActionEvent e) {
+ awtOnTurnEnded(false, true);
+ }
+ }
+
+
+ // BattleObserver methods are always called by battle thread
+ // but everything inside invokeLater {} block in on AWT thread
+ private class BattleObserver extends BattleAdaptor {
+
+ @Override
+ public void onTurnEnded(final TurnEndedEvent event) {
+ if (lastMajorEvent.get() == majorEvent.get()) {
+ // snapshot is updated out of order, but always within the same major event
+ snapshot.set(event.getTurnSnapshot());
+ }
+
+ final IRobotSnapshot[] robots = event.getTurnSnapshot().getRobots();
+
+ for (int i = 0; i < robots.length; i++) {
+ RobotSnapshot robot = (RobotSnapshot) robots[i];
+ final int r = i;
+ final String text = robot.getOutputStreamSnapshot();
+
+ if (text != null && text.length() != 0) {
+ robot.setOutputStreamSnapshot(null);
+ EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ synchronized (snapshot) {
+ outCache[r].append(text);
+ }
+ }
+ });
+ }
+ }
+ if (isPaused.get()) {
+ EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ awtOnTurnEnded(false, true);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onRoundStarted(final RoundStartedEvent event) {
+ if (lastMajorEvent.get() == majorEvent.get()) {
+ snapshot.set(event.getStartSnapshot());
+ }
+ majorEvent.incrementAndGet();
+ EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ awtOnTurnEnded(true, false);
+ battleEventDispatcher.onRoundStarted(event);
+ lastMajorEvent.incrementAndGet();
+ }
+ });
+ }
+
+ @Override
+ public void onBattleStarted(final BattleStartedEvent event) {
+ majorEvent.incrementAndGet();
+ EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ isRunning.set(true);
+ isPaused.set(false);
+ synchronized (snapshot) {
+ outCache = new StringBuilder[event.getRobotsCount()];
+ for (int i = 0; i < event.getRobotsCount(); i++) {
+ outCache[i] = new StringBuilder(1024);
+ }
+ }
+ snapshot.set(null);
+ battleEventDispatcher.onBattleStarted(event);
+ lastMajorEvent.incrementAndGet();
+ awtOnTurnEnded(true, false);
+ timerTask.start();
+ }
+ });
+ }
+
+ @Override
+ public void onBattleFinished(final BattleFinishedEvent event) {
+ majorEvent.incrementAndGet();
+ EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ isRunning.set(false);
+ isPaused.set(false);
+ timerTask.stop();
+ // flush text cache
+ awtOnTurnEnded(true, true);
+
+ battleEventDispatcher.onBattleFinished(event);
+ lastMajorEvent.incrementAndGet();
+ snapshot.set(null);
+
+ // paint logo
+ awtOnTurnEnded(true, true);
+ }
+ });
+ }
+
+ @Override
+ public void onBattleCompleted(final BattleCompletedEvent event) {
+ majorEvent.incrementAndGet();
+ EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ battleEventDispatcher.onBattleCompleted(event);
+ lastMajorEvent.incrementAndGet();
+ awtOnTurnEnded(true, true);
+ }
+ });
+ }
+
+ @Override
+ public void onRoundEnded(final RoundEndedEvent event) {
+ majorEvent.incrementAndGet();
+ EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ battleEventDispatcher.onRoundEnded(event);
+ lastMajorEvent.incrementAndGet();
+ awtOnTurnEnded(true, true);
+ }
+ });
+ }
+
+ @Override
+ public void onBattlePaused(final BattlePausedEvent event) {
+ EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ timerTask.stop();
+ battleEventDispatcher.onBattlePaused(event);
+ awtOnTurnEnded(true, true);
+ isPaused.set(true);
+ }
+ });
+ }
+
+ @Override
+ public void onBattleResumed(final BattleResumedEvent event) {
+ EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ battleEventDispatcher.onBattleResumed(event);
+ if (isRunning.get()) {
+ timerTask.start();
+ isPaused.set(false);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onBattleMessage(final BattleMessageEvent event) {
+ EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ battleEventDispatcher.onBattleMessage(event);
+ }
+ });
+ }
+
+ @Override
+ public void onBattleError(final BattleErrorEvent event) {
+ EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ battleEventDispatcher.onBattleError(event);
+ }
+ });
+ }
+ }
+}