]> source.dussan.org Git - aspectj.git/commitdiff
testcode for handles for files from multiple source folders
authoraclement <aclement>
Tue, 16 Sep 2008 23:10:50 +0000 (23:10 +0000)
committeraclement <aclement>
Tue, 16 Sep 2008 23:10:50 +0000 (23:10 +0000)
31 files changed:
tests/multiIncremental/MultiSource/base/src1/CodeOne.java [new file with mode: 0644]
tests/multiIncremental/MultiSource/base/src2/CodeTwo.java [new file with mode: 0644]
tests/multiIncremental/MultiSource/base/src3/pkg/CodeThree.java [new file with mode: 0644]
tests/multiIncremental/Simpler/base/Basic.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/coordination/Condition.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/coordination/CoordinationAction.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/coordination/Coordinator.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/coordination/Exclusion.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/coordination/MethodState.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/coordination/Mutex.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/coordination/Selfex.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/coordination/TimeoutException.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/spacewar/Bullet.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/spacewar/Debug.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/spacewar/Display.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/spacewar/Display1.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/spacewar/Display2.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/spacewar/EnergyPacket.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/spacewar/EnergyPacketProducer.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/spacewar/EnsureShipIsAlive.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/spacewar/Game.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/spacewar/GameSynchronization.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/spacewar/Pilot.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/spacewar/Player.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/spacewar/Registry.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/spacewar/RegistrySynchronization.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/spacewar/Robot.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/spacewar/SWFrame.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/spacewar/Ship.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/spacewar/SpaceObject.java [new file with mode: 0644]
tests/multiIncremental/SpaceWar/base/src/spacewar/Timer.java [new file with mode: 0644]

diff --git a/tests/multiIncremental/MultiSource/base/src1/CodeOne.java b/tests/multiIncremental/MultiSource/base/src1/CodeOne.java
new file mode 100644 (file)
index 0000000..23b4279
--- /dev/null
@@ -0,0 +1 @@
+public class CodeOne {}
diff --git a/tests/multiIncremental/MultiSource/base/src2/CodeTwo.java b/tests/multiIncremental/MultiSource/base/src2/CodeTwo.java
new file mode 100644 (file)
index 0000000..b04eb30
--- /dev/null
@@ -0,0 +1,3 @@
+package pkg;
+
+public class CodeTwo {}
diff --git a/tests/multiIncremental/MultiSource/base/src3/pkg/CodeThree.java b/tests/multiIncremental/MultiSource/base/src3/pkg/CodeThree.java
new file mode 100644 (file)
index 0000000..393ed67
--- /dev/null
@@ -0,0 +1,3 @@
+package pkg;
+
+public class CodeThree {}
diff --git a/tests/multiIncremental/Simpler/base/Basic.java b/tests/multiIncremental/Simpler/base/Basic.java
new file mode 100644 (file)
index 0000000..c1eac7a
--- /dev/null
@@ -0,0 +1,15 @@
+public aspect Basic {
+
+/*
+  before(): execution(* a(..)) {}
+  after(): execution(* a(..)) {}
+  before(): execution(* a(..)) {}
+  after(): execution(* a(..)) {}
+
+*/
+  public void foo() {
+         new Runnable() {
+                 public void run() {}
+         };
+  }
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/coordination/Condition.java b/tests/multiIncremental/SpaceWar/base/src/coordination/Condition.java
new file mode 100644 (file)
index 0000000..18bbafe
--- /dev/null
@@ -0,0 +1,37 @@
+/* -*- Mode: Java; -*-
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+*/
+
+package coordination;
+
+
+/**
+ * Interface for pre-conditions that are passed to guardedEntry methods of
+ * Coordinator.
+ * Conditions should be passed as anonymous classes that simply implement
+ * the checkit method.
+ *
+ */
+public interface Condition {
+
+    /**
+     * This method is called automatically by Coordinator.guardedEntry(...)
+     * and it's called everytime the coordination state changes.
+     */
+
+    public boolean checkit();
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/coordination/CoordinationAction.java b/tests/multiIncremental/SpaceWar/base/src/coordination/CoordinationAction.java
new file mode 100644 (file)
index 0000000..7825b95
--- /dev/null
@@ -0,0 +1,37 @@
+/* -*- Mode: Java; -*-
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+*/
+
+package coordination;
+
+
+/**
+ * Interface for coordination actions that are passed to guardedEntry methods of
+ * Coordinator.
+ * Coordination actions should be passed as anonymous classes that simply 
+ * implement the doit method. 
+ *
+ */
+public interface CoordinationAction {
+    /**
+     * This method is called  by Coordinator.guardedEntry(...) and
+     * Coordinator.guardedExit(...). Use it for changing coordination state
+     * upon entering and exiting methods.
+     */
+
+    public void doit();
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/coordination/Coordinator.java b/tests/multiIncremental/SpaceWar/base/src/coordination/Coordinator.java
new file mode 100644 (file)
index 0000000..ea0522d
--- /dev/null
@@ -0,0 +1,449 @@
+/* -*- Mode: Java; -*-
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+*/
+
+package coordination;
+
+import java.util.*;  //!!!
+
+/**
+ * The Coordinator class provides the basic functionality for synchronizing
+ * and coordinating different threads upon entering and exiting methods.
+ * It can be used in two different ways:
+ * 1) by instantiating regular coordinator objects that are used by aspects; or
+ * 2) by extending it (sub-classing) with coordinator aspects.
+ * <P>
+ * Method invocations are the smallest units for defining critical sections 
+ * and pre-conditions. The use of coordinators, either regular objects or aspect
+ * instances, should always end up by invoking guardedEntry(...) in a
+ * before weave and guardedExit(...) in an after weave for all methods that 
+ * need coordination. guardedEntry and guardedExit are the methods that
+ * actually manage the synchronization and coordination constraints given
+ * by their parameters and by pre-existent exclusion markers.
+ * <P>
+ * The synchronization of threads for the execution of critical section 
+ * methods in an object is done by marking those methods as self- and/or 
+ * mutually-exclusive (addSelfex, addMutex).
+ * Just by itself,  addSelfex("M") does not enforce the self-exclusion
+ * of method M - enforcement is done by invoking guardedEntry before
+ * M is executed. Similarly, addMutex(new String[] {"M1", "M2"}) does 
+ * not enforce the mutual exclusion between methods M1 and M2.
+ * <P>
+ * A guardedEntry on a method that has been marked as self-exclusive 
+ * ensures that the method is executed in the invoked object by only one thread 
+ * at a time. A guardedEntry on a method that has been marked has mutually-
+ * exclusive with other methods ensures that the execution of that method
+ * by a thread in the invoked object temporarily blocks the execution by
+ * other threads of the methods that are in the same mutex set.
+ * <P>
+ * The coordination of threads, i.e. their explicit suspension and 
+ * resumption, is done through the use of pre-conditions and coordination 
+ * actions that are passed as parameters to guardedEntry and guardedExit
+ * with the form of anonymous classes.
+ */
+public abstract aspect Coordinator {
+    private Hashtable methods = null;
+    private Vector exclusions = null;
+  
+    abstract protected pointcut synchronizationPoint();
+
+    public Coordinator() {
+       methods = new Hashtable();
+       exclusions = new Vector(5);
+    }
+
+    before (): synchronizationPoint() {
+       this.guardedEntry(thisJoinPointStaticPart.getSignature().getName());
+    }
+  
+    after (): synchronizationPoint() {
+       this.guardedExit(thisJoinPointStaticPart.getSignature().getName());
+    }
+
+    /**
+     * Takes a multi-part method name (eg "BoundedBuffer.put")
+     * and marks that method as self-exclusive.
+     * No checks are made with respect to the existence of the method
+     * whose name is given.
+     */
+    public synchronized void addSelfex(String methName) {
+       Selfex sex = new Selfex (methName);
+
+       // update db of all exclusions in this coordinator
+       exclusions.addElement(sex);
+
+       // update local info in method
+       Method aMeth = getOrSetMethod(methName);
+       aMeth.addExclusion(sex);
+    }
+
+    /**
+     * Takes a multi-part method name (e.g. "BoundedBuffer.put")
+     * and removes that method from the list of self-exclusive methods.
+     */
+    public synchronized void removeSelfex(String methName) {
+       for (int i = 0; i < exclusions.size(); i++) {
+           Exclusion sex = (Exclusion)exclusions.elementAt(i);
+           if ((sex instanceof Selfex) &&
+               (((Selfex)sex).methodName.equals(methName))) {
+
+               // update db of all exclusions in this coordinator 
+               exclusions.removeElementAt(i);
+
+               // update local info in method
+               Method aMeth = getOrSetMethod(methName);
+               aMeth.removeExclusion(sex);
+           }
+       }
+    }
+
+    /**
+     * Takes an array of multi-part method names and marks those
+     * methods as mutually exclusive.
+     * No checks are made with respect to the existence of the methods
+     * whose names are given.
+     */
+    public synchronized void addMutex(String[] methNames) {
+       Mutex mux = new Mutex(methNames);
+
+       // update db of all exclusions in this coordinator
+       exclusions.addElement(mux);
+
+       // update local info in each method
+       for (int i = 0; i < methNames.length; i++) {
+           Method aMeth = getOrSetMethod(methNames[i]);
+           aMeth.addExclusion(mux);
+       }
+    }
+
+    /**
+     * Takes an array of multi-part method names that correspond
+     * to an existing mutex set and remove the mutual exclusion constraint.
+     * If the given mutex set does not exist, removeMutex does nothing.
+     */
+    public synchronized void removeMutex(String[] methNames) {
+       for (int i = 0; i < exclusions.size(); i++) {
+           Exclusion mux = (Exclusion)exclusions.elementAt(i);
+           if (mux instanceof Mutex) {
+               boolean same = true;
+               for (int j = 0; j < methNames.length; j++)
+                   if (!methNames[j].equals(((Mutex)mux).methodNames[j]))
+                       same = false;
+               if (same) {
+                   // update db of all exclusions in this coordinator
+                   exclusions.removeElementAt(i);
+
+                   // update local info in each method involved
+                   for (int j = 0; j < methNames.length; j++) {
+                       Method aMeth = getOrSetMethod(methNames[j]);
+                       aMeth.removeExclusion(mux);
+                   }
+               }
+           }
+       }
+    }
+
+    /**
+     * This method is the guard for enforcing all synchronization and
+     * coordination constraints of a given method, and it should be called
+     * just before the method is executed.
+     * In this form, only the method name is given. The only constraints
+     * checked are the exclusion constraints.
+     * If the method was previousely marked as selfex (through addSelfex),
+     * guardedEntry ensures that the method is executed only when no other 
+     * thread is executing it.
+     * If the method was previousely marked as being in one or more mutex
+     * sets, guardedEntry ensures that the method is executed only when no other 
+     * thread is executing any of the methods with which the give method is
+     * mutexed.
+     */
+    public synchronized void guardedEntry(String methName) {
+       guardedEntry(methName,  new Condition() {
+               public boolean checkit() {
+                   return true;
+               }
+           }, null);
+    }
+
+    /**
+     * Just like guardedEntry(String methName), but the given method is executed
+     * only when the given condition is true.
+     * guardedEntry is the guard for enforcing all synchronization and
+     * coordination constraints of a given method, and it should be called
+     * just before the method is executed.
+     * In this form, the method name is given along with a condition. 
+     * The constraints checked are the exclusion constraints and whether
+     * the given condition is true.
+     * If the method was previousely marked as selfex (through addSelfex),
+     * guardedEntry ensures that the method is executed only when no other 
+     * thread is executing it.
+     * If the method was previousely marked as being in one or more mutex
+     * sets, guardedEntry ensures that the method is executed only when no other 
+     * thread is executing any of the methods with which the give method is
+     * mutexed.
+     * If the condition is false, guardedEntry suspends the current thread.
+     * That thread remains suspended until the condition becomes true, in
+     * which case all constraints are rechecked before the method is executed.
+     * When all exclusion constraints are checked and the given condition is
+     * true, the given method is executed.
+     */
+    public synchronized void guardedEntry(String methName, Condition condition) {
+       guardedEntry(methName, condition, null);
+    }
+
+    /**
+     * Just like guardedEntry(String methName), but with an additional
+     * coordination action that is executed before the given method is
+     * executed.
+     * guardedEntry is the guard for enforcing all synchronization and
+     * coordination constraints of a given method, and it should be called
+     * just before the method is executed.
+     * In this form, the method name is given along with a coordination action. 
+     * The only constraints checked are the exclusion constraints.
+     * If the method was previousely marked as selfex (through addSelfex),
+     * guardedEntry ensures that the method is executed only when no other 
+     * thread is executing it.
+     * If the method was previousely marked as being in one or more mutex
+     * sets, guardedEntry ensures that the method is executed only when no other 
+     * thread is executing any of the methods with which the give method is
+     * mutexed.
+     * The given coordination action is executed just before the given method 
+     * is executed.
+     */
+    public synchronized void guardedEntry(String methName, 
+                                         CoordinationAction action) {
+       guardedEntry(methName, new Condition() {
+               public boolean checkit() {
+                   return true;
+               }
+           }, 
+                    action);
+    }
+
+    /**
+     * Just like guardedEntry(String methName), but the given method is executed
+     * only when the given condition is true; the additional
+     * coordination action that is executed before the given method is
+     * executed.
+     * guardedEntry is the guard for enforcing all synchronization and
+     * coordination constraints of a given method, and it should be called
+     * just before the method is executed.
+     * In this form, the method name is given along with a condition and
+     * a coordination action. 
+     * The constraints checked are the exclusion constraints and whether the
+     * given condition is true.
+     * If the method was previousely marked as selfex (through addSelfex),
+     * guardedEntry ensures that the method is executed only when no other 
+     * thread is executing it.
+     * If the method was previousely marked as being in one or more mutex
+     * sets, guardedEntry ensures that the method is executed only when no other 
+     * thread is executing any of the methods with which the give method is
+     * mutexed.
+     * If the condition is false, guardedEntry suspends the current thread.
+     * That thread remains suspended until the condition becomes true, in
+     * which case all constraints are rechecked before the method is executed.
+     * When all exclusion constraints are checked and the given condition is
+     * true, the given method is executed.
+     * The given coordination action is executed just before the given method 
+     * is executed.
+     */
+    public synchronized void guardedEntry(String methName, 
+                                         Condition condition,
+                                         CoordinationAction action) {
+       Method aMeth = getOrSetMethod(methName);
+       boolean canGo = false;
+
+       // test pre-conditions for entering the method
+       while (!canGo) {
+           canGo = true;
+           for (int i = 0; i < aMeth.exes.size() && canGo; i++)
+               if (!((Exclusion)aMeth.exes.elementAt(i)).testExclusion(aMeth.name)) {
+                   canGo = false;
+               }
+           if (canGo && !condition.checkit()) {
+               canGo = false;
+           }
+           if (!canGo)
+               try {
+                   wait();
+               } catch (InterruptedException e) { }
+       }
+
+       // OK.
+       enterMethod(aMeth, action);
+    }
+
+    /**
+     * This method is similar to guardedEntry, but it takes
+     * an additional parameter - the milliseconds after which any suspension
+     * will abort with a timeout.
+     */
+    public synchronized void guardedEntryWithTimeout(String methName,
+                                                    long millis) 
+                                throws TimeoutException {
+       guardedEntryWithTimeout(methName, new Condition() {
+               public boolean checkit() {
+                   return true;
+               }
+           }, null, millis);
+    }
+
+    /**
+     * This method is similar to guardedEntry, but it takes
+     * an additional parameter - the milliseconds after which any suspension
+     * will abort with a timeout.
+     */
+    public synchronized void guardedEntryWithTimeout(String methName, 
+                                                    Condition condition,
+                                                    long millis) 
+                                throws TimeoutException {
+       guardedEntryWithTimeout(methName, condition, null, millis);
+    }
+
+    /**
+     * This method is similar to guardedEntry, but it takes
+     * an additional parameter - the milliseconds after which any suspension
+     * will abort with a timeout.
+     */
+    public synchronized void guardedEntryWithTimeout(String methName, 
+                                                    CoordinationAction action,
+                                                    long millis) 
+                                throws TimeoutException {
+       guardedEntryWithTimeout(methName, new Condition() {
+               public boolean checkit() {
+                   return true;
+               }
+           }, action, millis);
+    }
+
+    /**
+     * This method is similar to guardedEntry, but it takes
+     * an additional parameter - the milliseconds after which any suspension
+     * will abort with a timeout.
+     */
+    public synchronized void guardedEntryWithTimeout(String methName, 
+                                                    Condition condition,
+                                                    CoordinationAction action,
+                                                    long millis) 
+                                throws TimeoutException {
+
+       Method aMeth = getOrSetMethod(methName);
+       boolean canGo = false;
+       long waitTime = millis;
+       long startTime = System.currentTimeMillis();
+
+       // test pre-conditions for entering the method
+       while (!canGo) {
+           canGo = true;
+           for (int i = 0; i < aMeth.exes.size() && canGo; i++)
+               if ((!((Exclusion)aMeth.exes.elementAt(i)).testExclusion(aMeth.name)) ||
+                   (!condition.checkit())) {
+                   canGo = false;
+               }
+           if (!canGo) {
+               try {
+                   wait(waitTime);
+               } catch (InterruptedException e) {}
+
+               long now = System.currentTimeMillis();
+               long timeSoFar = now - startTime;
+               if (timeSoFar >= millis) // timeout!
+                   throw new TimeoutException(timeSoFar);
+               else // adjust time
+                   waitTime = millis - timeSoFar;
+           }
+       }
+
+       // OK.
+       enterMethod(aMeth, action);
+    }
+
+    /**
+     * This method provides the means for updating all synchronization and
+     * coordination state after the execution of a given method, and it should be 
+     * called after the method is executed.
+     * In this form, only the method name is given.
+     * The synchronization state for self- and mutual-exclusion is 
+     * automatically upadted.
+     */
+    public synchronized void guardedExit(String methName) {
+       guardedExit(methName, null);
+    }
+
+    /**
+     * Just like guardedExit(String methName) but with an additional
+     * coordination action that is executed.
+     * guardedExit provides the means for updating all synchronization and
+     * coordination state after the execution of a given method, and it should be 
+     * called after the method is executed.
+     * In this form, the method name is given along with a coordination action.
+     * The synchronization state for self- and mutual-exclusion is 
+     * automatically upadted.
+     * The given coordination action is executed.
+     */
+    public synchronized void guardedExit(String methName, 
+                                        CoordinationAction action) {
+       Method aMeth = getOrSetMethod(methName);
+
+       for (int i = 0; i < aMeth.exes.size(); i++)
+           ((Exclusion)aMeth.exes.elementAt(i)).exitExclusion(methName);
+       if (action != null) action.doit();
+       notifyAll();
+    }
+
+    private Method getOrSetMethod(String methName) {
+       Method aMeth = null;
+       if (!methods.containsKey(methName)) {
+           methods.put(methName, (aMeth = new Method(methName)));
+       }
+       else {
+           aMeth = (Method) methods.get(methName);
+       }
+       return aMeth;
+    }
+
+    private void enterMethod(Method aMeth, CoordinationAction action) {
+       for (int i = 0; i < aMeth.exes.size(); i++)
+           ((Exclusion)aMeth.exes.elementAt(i)).enterExclusion(aMeth.name);
+
+       if (action != null) action.doit();
+    }
+
+
+
+}
+
+class Method {
+  String name;
+  Vector exes = new Vector(3);
+
+  Method(String n) {
+    name = n;
+  }
+
+  void addExclusion(Exclusion ex) {
+    exes.addElement(ex);
+  }
+
+  void removeExclusion(Exclusion ex) {
+    for (int i = 0; i < exes.size(); i++) {
+      if (exes.elementAt(i) == ex)
+       exes.removeElementAt(i);
+    }
+  }
+}
+
diff --git a/tests/multiIncremental/SpaceWar/base/src/coordination/Exclusion.java b/tests/multiIncremental/SpaceWar/base/src/coordination/Exclusion.java
new file mode 100644 (file)
index 0000000..9179cd6
--- /dev/null
@@ -0,0 +1,33 @@
+/* -*- Mode: Java; -*-
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+*/
+
+package coordination;
+
+
+interface Exclusion {
+
+    boolean testExclusion(String methodName);
+
+    void enterExclusion(String methodName);
+
+    void exitExclusion(String methodName);
+
+    // for debug    !!!
+    void printNames();
+}
+
diff --git a/tests/multiIncremental/SpaceWar/base/src/coordination/MethodState.java b/tests/multiIncremental/SpaceWar/base/src/coordination/MethodState.java
new file mode 100644 (file)
index 0000000..03a4437
--- /dev/null
@@ -0,0 +1,45 @@
+/* -*- Mode: Java; -*-
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+*/
+
+package coordination;
+
+import java.util.Vector;
+import java.util.Enumeration;
+
+
+class MethodState {
+
+    Vector threads=new Vector();
+
+    void enterInThread (Thread t) {
+       threads.addElement(t);
+    }
+
+    void exitInThread(Thread t) {
+       threads.removeElement(t);
+    }
+
+    boolean hasOtherThreadThan(Thread t) {
+       Enumeration e = threads.elements();
+       while (e.hasMoreElements())
+           if (e.nextElement() != t)
+               return(true);
+       return (false);
+    }
+
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/coordination/Mutex.java b/tests/multiIncremental/SpaceWar/base/src/coordination/Mutex.java
new file mode 100644 (file)
index 0000000..2472137
--- /dev/null
@@ -0,0 +1,86 @@
+/* -*- Mode: Java; -*-
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+*/
+
+package coordination;
+
+import java.lang.String;
+
+
+class Mutex implements Exclusion {
+    String[] methodNames;
+    MethodState[] methodStates;
+
+    String prettyName;
+  
+    Mutex (String[] _methodNames) {
+       methodNames = _methodNames;
+       methodStates = new MethodState[methodNames.length];
+       for (int i = 0; i < methodNames.length; i++) {
+           methodStates[i] = new MethodState();
+       }
+    }
+
+    private boolean isMethodIn (String _methodName) {
+       for (int i = 0; i < methodNames.length; i++) {
+           if (_methodName.equals(methodNames[i])) 
+               return(true);
+       }
+       return(false);
+    }
+
+    private MethodState getMethodState (String _methodName) {
+       for (int i = 0; i < methodNames.length; i++) {
+           if (_methodName.equals(methodNames[i])) 
+               return(methodStates[i]);
+       }
+       return(null);
+    }
+
+    public boolean testExclusion (String _methodName) {
+       Thread ct = Thread.currentThread();
+       //
+       // Loop through each of the other methods in this exclusion set, to be sure
+       // that no other thread is running them.  Note that we have to be careful
+       // about selfex.
+       // 
+       for (int i = 0; i < methodNames.length; i++) {
+           if (!_methodName.equals(methodNames[i])) {
+               if (methodStates[i].hasOtherThreadThan(ct))
+                   return(false);
+           }
+       }
+       return (true);
+    }
+
+    public void enterExclusion (String _methodName) {
+       MethodState methodState = getMethodState(_methodName);
+       methodState.enterInThread(Thread.currentThread());
+    }
+
+    public void exitExclusion (String _methodName) {
+       MethodState methodState = getMethodState(_methodName);
+       methodState.exitInThread(Thread.currentThread());
+    }
+
+    public void printNames() {
+       System.out.print("Mutex names: ");
+       for (int i = 0; i < methodNames.length; i++)
+           System.out.print(methodNames[i] + " ");
+       System.out.println();
+    }
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/coordination/Selfex.java b/tests/multiIncremental/SpaceWar/base/src/coordination/Selfex.java
new file mode 100644 (file)
index 0000000..ff73afd
--- /dev/null
@@ -0,0 +1,55 @@
+/* -*- Mode: Java; -*-
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+*/
+
+package coordination;
+
+
+import java.lang.String;
+
+class Selfex implements Exclusion {
+    String methodName;
+    Thread thread;
+    int count = 0;
+
+    Selfex (String _methodName) {
+       methodName = _methodName;
+    }
+
+    public boolean testExclusion (String _methodName) {
+       if (count == 0)
+           return(true);
+       return (thread == Thread.currentThread());
+    }
+
+    public void enterExclusion (String _methodName) {
+       count++;
+       thread = Thread.currentThread();    // note that if count wasn't 0
+       // we aren't changing thread
+    }
+
+    public void exitExclusion (String _methodName) {
+       count--;
+       if (count == 0)                   // not stricly necessary, but...
+           thread = null;
+    }
+
+    public void printNames() {
+       System.out.println("Selfex name: " + methodName);
+    }
+
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/coordination/TimeoutException.java b/tests/multiIncremental/SpaceWar/base/src/coordination/TimeoutException.java
new file mode 100644 (file)
index 0000000..e16aa7f
--- /dev/null
@@ -0,0 +1,27 @@
+/* -*- Mode: Java; -*-
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+*/
+
+package coordination;
+
+
+public class TimeoutException extends Exception {
+    long time;
+    TimeoutException(long _time) {
+       time = _time;
+    }
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/spacewar/Bullet.java b/tests/multiIncremental/SpaceWar/base/src/spacewar/Bullet.java
new file mode 100644 (file)
index 0000000..6581dbb
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+
+Bullet.java
+Part of the Spacewar game.
+
+*/
+
+package spacewar;
+
+class Bullet extends SpaceObject {
+
+    static private final int SIZE = 3;        //Can't be changed for now!!!
+    static private int LIFETIME = 50;
+
+    private int lifeLeft;
+
+    Bullet (Game theGame, double xP, double yP, double xV, double yV) {
+        super(theGame, xP, yP, xV, yV);
+        lifeLeft = LIFETIME;
+    }
+
+    int getSize() { return SIZE; }
+
+    void handleCollision(SpaceObject obj) {
+        die();
+    }
+
+    void clockTick() {
+        if (--lifeLeft == 0)
+            die();
+        super.clockTick();
+    }
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/spacewar/Debug.java b/tests/multiIncremental/SpaceWar/base/src/spacewar/Debug.java
new file mode 100644 (file)
index 0000000..a3afb1d
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+Debug.java
+Part of the Spacewar system.
+
+*/
+
+package spacewar;
+
+import java.awt.Menu;
+import java.awt.CheckboxMenuItem;
+import java.awt.Frame;
+import java.awt.TextArea;
+import java.awt.Dimension;
+
+/**
+ * This aspect specifies debugging information to be output to the
+ * information window.
+ *
+ * When the debug aspect is compiled in the Frame menu has several checkbox
+ * items that can be used to control the amount of tracing information
+ * displayed.  (By default the first three are off, because they generate
+ * so much information.)
+ *
+ * There are two reasons to gather all this debugging code into an aspect
+ * like this:
+ *
+ *   (1) It makes it easier to understand when it is all in one place.
+ *
+ *   (2) It means that we can "plug and debug".  We can enable/disable
+ *       the debugging code simply by weaving or not weaving this
+ *       aspect in.
+ *
+ * All in all, this is a lot better than the usual practice of writing
+ * complex debugging code and then deleting it when the bug is found,
+ * only to regret it a month later when a related bug surfaces.  (Or even
+ * the same bug!)
+ *
+ * This file also defines a class InfoWin, which it uses to display all the
+ * debugging information.
+ */
+aspect Debug {
+
+    private static InfoWin infoWin = new InfoWin();
+
+    private static Menu menu = new Menu("Debug");
+
+    private static CheckboxMenuItem traceConstructors =
+        new CheckboxMenuItem("trace constructors", false);
+    private static CheckboxMenuItem traceInitializations =
+        new CheckboxMenuItem("trace initializations", false);
+    private static CheckboxMenuItem traceMethods =
+        new CheckboxMenuItem("trace methods", false);
+    private static CheckboxMenuItem traceClockTick =
+        new CheckboxMenuItem("trace clock tick", false);
+    private static CheckboxMenuItem traceRegistry =
+        new CheckboxMenuItem("trace registry", true);
+    private static CheckboxMenuItem traceFireCollideDamage =
+        new CheckboxMenuItem("trace fire, collide, damage", true);
+
+    after() returning (SWFrame frame): call(SWFrame+.new(..)) {
+        menu.add(traceConstructors);
+        menu.add(traceInitializations);
+        menu.add(traceMethods);
+        menu.add(traceClockTick);
+        menu.add(traceRegistry);
+        menu.add(traceFireCollideDamage);
+        frame.getMenuBar().add(menu);
+    }
+
+    /*
+     * all constructors
+     */
+    pointcut allConstructorsCut(): 
+       call((spacewar.* && !(Debug+ || InfoWin+)).new(..));
+
+    before(): allConstructorsCut() {
+        if (traceConstructors.getState()) {
+            infoWin.println("begin constructing " + thisJoinPoint.getSignature());
+        }
+    }
+
+    after() returning: allConstructorsCut() {
+        if (traceConstructors.getState()) {
+            infoWin.println("done constructing " + thisJoinPoint.getSignature());
+        }
+    }
+
+    /*
+     * All dynamic initializations
+     */
+    pointcut allInitializationsCut(): 
+       initialization((spacewar.* && !(Debug+ || InfoWin+)).new(..));
+
+    before(): allInitializationsCut() {
+        if (traceConstructors.getState()) {
+            infoWin.println("begin initializing " + thisJoinPoint.getSignature());
+        }
+    }
+    after() returning : allInitializationsCut() {
+        if (traceConstructors.getState()) {
+            infoWin.println("done initializing " + thisJoinPoint.getSignature());
+        }
+    }
+
+    /*
+     * all methods
+     */
+    pointcut allMethodsCut(): 
+        execution(* (spacewar.* && !(Debug+ || InfoWin+)).*(..));
+
+    before(): allMethodsCut() {
+        if (traceMethods.getState()) {
+            infoWin.println("entering " + thisJoinPoint.getSignature());
+        }
+    }
+    after() returning : allMethodsCut() {
+        if (traceMethods.getState()) {
+            infoWin.println("exiting " + thisJoinPoint.getSignature());
+        }
+    }
+
+    /*
+     * clock ticks
+     */
+    after(Object obj) returning :
+        (target(obj) && (target(Game) ||
+                        target(Registry) ||
+                        target(SpaceObject)))
+        && call(void clockTick()) {
+        if (traceClockTick.getState())
+            infoWin.println("ticking " + obj);
+    }
+
+    /*
+     * registry contents
+     */
+    after(Registry registry) returning :
+        target(registry) && (call(void register(..)) ||
+                            call(void unregister(..))) {
+        if (traceRegistry.getState())
+            infoWin.println(registry.getTable().size() +
+                            " space objects in the registry.");
+    }
+
+    /*
+     * fire, collide, damage
+     */
+    after() returning : call(void Ship.fire()) {
+        if (traceFireCollideDamage.getState())
+            infoWin.println("firing");
+    }
+
+    after(Ship ship, SpaceObject obj) returning :
+       call(void handleCollision(SpaceObject)) && target(ship) && args(obj) {
+        if (traceFireCollideDamage.getState())
+            infoWin.println(ship + " collides with " + obj);
+    }
+
+    after(Ship shipA, Ship shipB) returning :
+        execution(void Ship.bounce(Ship, Ship)) && args(shipA, shipB) {
+        if (traceFireCollideDamage.getState())
+            infoWin.println(shipA + " bounces with " + shipB);
+    }
+
+    before(Ship ship, double amount):
+       call(void Ship.inflictDamage(double)) && target(ship) && args(amount) {
+        if (traceFireCollideDamage.getState())
+            if (amount > 0)
+                infoWin.println(ship + "gets " +
+                                amount + " damage (" +
+                                ship.getDamage() + ")");
+    }
+
+}
+
+class InfoWin {
+    private Frame    frame;
+    private TextArea info;
+
+    InfoWin() {
+        frame = new Frame("debugging info for spacewar game");
+        info = new TextArea();
+        info.setEditable(false);
+
+        Dimension screenSize = frame.getToolkit().getScreenSize();
+        frame.setSize(250, 600);
+        frame.setLocation(screenSize.width - 250, 0);
+        frame.add(info);
+        frame.show();
+        frame.toFront();
+    }
+
+    void clear() {
+        info.setText("");
+    }
+
+    void println(String line) {
+        info.append(line + "\n");
+    }
+
+    void print(String line) {
+        info.append(line);
+    }
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/spacewar/Display.java b/tests/multiIncremental/SpaceWar/base/src/spacewar/Display.java
new file mode 100644 (file)
index 0000000..4236407
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+
+Display.java
+Part of the Spacewar system.
+*/
+
+package spacewar;
+
+import java.util.Vector;
+import java.util.Enumeration;
+import java.awt.Graphics;
+import java.awt.Canvas;
+import java.awt.Image;
+
+/**
+ * The display aspects capture the look and feel of the Game in modular
+ * pluggable units.
+ *
+ * The model is that constructing a concrete subclass of Display attaches that
+ * kind of display to the game.  It will Display the game as it goes along.
+ * A game can have any number of displays.  Any of the displays will accept
+ * keyboard input.
+ *
+ */
+
+class Display  extends Canvas {
+
+    private static Vector DISPLAYS = new Vector(2);
+    private static Vector PLAYERS  = new Vector(2);
+    private static Pilot  pilot1, pilot2;
+
+    Game     game;
+    SWFrame  frame;
+    Image    offImage;
+    Graphics offGraphics;
+
+    Game  getGame()   { return game; }
+    static Pilot getPilot1() { return pilot1; }
+    static Pilot getPilot2() { return pilot2; }
+
+    Display(Game g) {
+        super();
+        game = g;
+
+        frame = new SWFrame(game, this);
+        DISPLAYS.addElement(this);
+    }
+
+
+    void noticeSizeChange() {
+        initializeOffImage();
+    }
+
+    private void initializeOffImage () {
+        int w = getSize().width;
+        int h = getSize().height;
+        if ( w > 0 & h > 0) {
+            offImage = createImage(w, h);
+            offGraphics = offImage.getGraphics();
+        }
+    }
+
+    /*
+     * In our double buffering scheme, painting just means copying the buffer
+     * to the screen.  The Display aspect draws into the buffer.
+     */
+    public void paint(Graphics g) {
+        if (offImage != null)
+            g.drawImage(offImage, 0, 0, null);
+    }
+
+    public void update(Graphics g) {
+        /*
+         * There are 4 steps to this:
+         *  - clear the double buffer
+         *  - paint the objects into the double buffer
+         *  - paint the status into the double buffer
+         *  - paint the doublebuffer into the buffer
+         */
+        offGraphics.setColor(getBackground());
+        offGraphics.fillRect(0, 0, getBounds().width, getBounds().height);
+        paintObjects(offGraphics);
+        paintStatus(offGraphics);
+        g.drawImage(offImage, 0, 0, null);
+    }
+
+    void paintObjects(Graphics g) { }
+    void paintStatus(Graphics g) {}
+
+    static aspect DisplayAspect {
+
+        after (String mode) returning (Game game): call(Game+.new(String)) && args(mode) {
+            new Display1(game);
+            new Display2(game);
+
+            if ( mode.equals("1") ) {
+                pilot1 = game.newPlayer(1);
+            }
+            else if ( mode.equals("2") ) {
+                pilot1 = game.newPlayer(1);
+                pilot2 = game.newPlayer(2);
+            }
+            else if (mode. equals("demo")) {
+                pilot1 = game.newRobot(1);
+                pilot2 = game.newRobot(2);
+            } else {
+                game.error("Invalid mode: " + mode);
+                game.quit();
+            }
+        }
+
+
+        /*
+         * I'm not really sure this belongs here.
+         *
+         * Being here what it does is makes the Display aspect
+         * responsible for having the Players couple up to it.  That's
+         * kind of nice, but its a bit incomplete, since Player is
+         * really part of the GUI, not part of the core Game.
+         *
+         * In a future re-factoring this will get worked out better.
+         * What will happen is that GUI will be an aspect that has the
+         * core GUI.  Each of the different kinds of displays will be
+         * aspects that tie themselves in.
+         */
+        after () returning (Player player): call(Player+.new(..)) {
+            Enumeration elements = DISPLAYS.elements();
+            while ( elements.hasMoreElements() ) {
+                Display display = (Display)elements.nextElement();
+                display.addKeyListener(player);
+            }
+        }
+
+        after() returning (Display display): call(Display+.new(..)) {
+            display.noticeSizeChange();
+        }
+
+        after(Display display) returning (): call(void setSize(..)) && target(display) {
+            display.noticeSizeChange();
+        }
+
+        after() returning : call(void Game.clockTick()) {
+            Enumeration elements = DISPLAYS.elements();
+            while ( elements.hasMoreElements() ) {
+                Display display = (Display)elements.nextElement();
+                display.repaint();
+            }
+        }
+    }
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/spacewar/Display1.java b/tests/multiIncremental/SpaceWar/base/src/spacewar/Display1.java
new file mode 100644 (file)
index 0000000..622f7d0
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+
+Display1.java
+Part of the Spacewar system.
+*/
+
+package spacewar;
+
+
+import java.awt.Graphics;
+import java.awt.Color;
+import java.util.Random;
+
+/**
+ * This is the standard display aspect.
+ */
+class Display1 extends Display {
+    /*
+     * Here's the color scheme for the game.  No other places in this file
+     * should say Color.xxx.  Instead, that color should be given a symbolic
+     * name here.
+     */
+    private static Color backgroundColor        = Color.black;
+    private static Color player1ShipColor       = Color.white;
+    private static Color player2ShipColor       = Color.gray;
+    private static Color robotShipColor         = new Color(0xa00000);
+    private static Color flameColor             = Color.red;
+    private static Color shipExplosionColor     = Color.red;
+    private static Color bulletColor            = Color.green;
+    private static Color energyPacketOuterColor = Color.blue;
+    private static Color energyPacketInnerColor = new Color(0x7070FF);
+    private static Color statusLabelsColor      = Color.white;
+    private static Color statusMeterBorderColor = Color.white;
+    private static Color energyStatusMeterColor = Color.blue;
+    private static Color damageStatusMeterColor = Color.red;
+
+
+    Display1(Game game) {
+        super(game);
+        frame.setLocation(20, 20);
+    }
+
+    void noticeSizeChange() {
+        super.noticeSizeChange();
+        setBackground(backgroundColor);
+    }
+
+    void paintObjects(Graphics g) {
+        SpaceObject[] objects = game.getRegistry().getObjects();
+        final int len = objects.length;
+        for (int i = 0; i < len; i++) {
+            objects[i].paint(g);
+        }
+    }
+
+    static aspect SpaceObjectPainting {
+
+        abstract private void SpaceObject.paint(Graphics g);
+
+        /*
+         * Ships are by far and away the most complex of the space Objects
+         * to paint.  First off, we need to set the color when the ship
+         * is made.
+         */
+        private Color Ship.color;
+
+        after(Pilot pilot) returning (Ship ship): call(Ship Game.newShip(Pilot)) && args(pilot) {
+            if (pilot.getNumber() == 1)
+                ship.color = player1ShipColor;
+            else if (pilot.getNumber() == 2)
+                ship.color = player2ShipColor;
+            else
+                ship.color = robotShipColor;
+        }
+
+        private void Ship.paint(Graphics g) {
+            final double PI  = Math.PI;
+            int[] radius     = {15, 12, -4, 12, -9, -15, -9};
+            double[] angle   = {0, PI * 3/4, 0, -PI * 3/4, PI/8, 0, -PI/8};
+            int[] x;
+            int[] y;
+
+            Random  random = new Random();
+
+            if (this.getDamage() >= this.MAX_DAMAGE) {
+                int lines = 20;
+                x = new int[lines];
+                y = new int[lines];
+                g.setColor(shipExplosionColor);
+                for (int i = 0; i < lines; i++) {
+                    x[i] = (int)(this.getXPos()) + random.nextInt() % 20;
+                    y[i] = (int)(this.getYPos()) + random.nextInt() % 20;
+                }
+                for (int i = 0; i < lines; i++)
+                    g.drawLine(x[i], y[i], x[(i + 1) % lines], y[(i + 1) % lines]);
+            } else {
+                x = new int[7];
+                y = new int[7];
+
+                g.setColor(this.color);
+
+                radius[5] += random.nextInt() % 3;
+                // convert coordinates from polar to cartesian
+                for (int i = 0; i < 7; i++) {
+                    x[i] = (int)
+                        (this.getXPos() +
+                         Math.cos(this.getOrientation() + angle[i]) * radius[i]);
+                    y[i] = (int)
+                        (this.getYPos() +
+                         Math.sin(this.getOrientation() + angle[i]) * radius[i]);
+                }
+
+                // draw the body as a polygon
+                g.drawPolygon(x, y, 4);
+
+                // if the ship is accelerating, draw in a flame
+                if (this.getRAcc() != 0) {
+                    g.setColor(flameColor);
+                    g.drawLine(x[4], y[4], x[5], y[5]);
+                    g.drawLine(x[5], y[5], x[6], y[6]);
+                }
+            }
+        }
+
+        /*
+         * Bullets
+         */
+        private void Bullet.paint(Graphics g) {
+            g.setColor(bulletColor);
+            g.fillOval((int)this.getXPos() - 1,
+                       (int)this.getYPos() - 1,
+                       3,
+                       3);
+        }
+
+        /*
+         * energy packets
+         */
+        private void EnergyPacket.paint(Graphics g) {
+            g.setColor(energyPacketOuterColor);
+            g.fillOval((int)this.getXPos() - 5,
+                       (int)this.getYPos() - 5,
+                       10, 10);
+            g.setColor(energyPacketInnerColor);
+            g.fillOval((int)this.getXPos() - 2,
+                       (int)this.getYPos() - 2,
+                       3, 3);
+        }
+    }
+
+
+    void paintStatus(Graphics g) {
+        int left1 = 60;
+        int top1  = 0;
+
+        int left2 = 200;
+        int top2  = 0;
+
+        g.setColor(statusLabelsColor);
+        g.drawString("energy:", 5, top1 + 15);
+        g.drawString("damage:", 5, top1 + 30);
+
+        if (getPilot1() != null)
+            paintLevels(g, getPilot1().getShip(), top1, left1);
+        if (getPilot2() != null)
+            paintLevels(g, getPilot2().getShip(), top2, left2);
+    }
+
+    static void paintLevels(Graphics g, Ship ship, int top, int left) {
+        if (ship == null)
+            return;
+        else if (ship.isAlive()) {
+            g.setColor(statusMeterBorderColor);
+            g.drawRect(left, top + 6,  101, 10);
+            g.drawRect(left, top + 21, 101, 10);
+            g.setColor(energyStatusMeterColor);
+            g.fillRect(left + 1, top + 7,  (int)(ship.getEnergyLevel()*100), 9);
+            g.setColor(damageStatusMeterColor);
+            g.fillRect(left + 1, top + 22, (int)(ship.getDamageLevel()*100), 9);
+        }
+        else {
+            g.setColor(damageStatusMeterColor);
+            g.drawString("Ship is destroyed", left+1, top+15);
+        }
+    }
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/spacewar/Display2.java b/tests/multiIncremental/SpaceWar/base/src/spacewar/Display2.java
new file mode 100644 (file)
index 0000000..0430a1c
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+
+Display2.java
+Part of the Spacewar system.
+*/
+
+package spacewar;
+
+
+import java.awt.Graphics;
+import java.awt.Color;
+
+
+/**
+ * This is the cheap Display aspect.
+ */
+class Display2 extends Display {
+
+    Display2(Game game) {
+        super(game);
+        frame.setLocation(540, 20);
+    }
+
+    void noticeSizeChange() {
+        super.noticeSizeChange();
+        setBackground(Color.darkGray);
+    }
+
+    void paintObjects(Graphics g) {
+        SpaceObject[] objects = game.getRegistry().getObjects();
+        final int len = objects.length;
+        for (int i = 0; i < len; i++) {
+            objects[i].paint(g);
+        }
+    }
+
+    static aspect SpaceObjectPainting {
+
+        abstract private void SpaceObject.paint(Graphics g);
+
+        /*
+         * Ships are by far and away the most complex of the space Objects
+         * to paint.
+         */
+        private Color Ship.color;
+
+        after(Pilot pilot) returning (Ship ship): call(Ship Game.newShip(Pilot)) && args(pilot) {
+            if (pilot.getNumber() == 1)
+                ship.color = Color.white;
+            else if (pilot.getNumber() == 2)
+                ship.color = Color.gray;
+            else
+                ship.color = new Color(0xa00000);
+        }
+
+        private void Ship.paint(Graphics g) {
+            if (this.getDamage() < this.MAX_DAMAGE) {
+                double x        = this.getXPos();
+                double y        = this.getYPos();
+                double sinTheta = Math.sin(this.getOrientation());
+                double cosTheta = Math.cos(this.getOrientation());
+
+                g.setColor(color);
+                g.drawLine((int)(x + 8*cosTheta), (int)(y + 8*sinTheta),
+                           (int)(x - 8*cosTheta), (int)(y - 8*sinTheta));
+
+                // if the ship is accelerating, draw thruster
+                if (this.getRAcc() != 0) {
+                    g.setColor(Color.red);
+                    g.fillOval((int)(x - 8*cosTheta), (int)(y - 8*sinTheta), 6, 6);
+                }
+            }
+        }
+
+        private void Bullet.paint(Graphics g) {
+            g.setColor(Color.green);
+            g.fillOval((int)this.getXPos() - 1,
+                       (int)this.getYPos() - 1,
+                       3,
+                       3);
+        }
+
+        private void EnergyPacket.paint(Graphics g) {
+            g.setColor(Color.white);
+            g.fillOval((int)this.getXPos() - 5,
+                       (int)this.getYPos() - 5,
+                       10,
+                       10);
+        }
+    }
+
+    void paintStatus(Graphics g) {
+        int left1 = 60;
+        int top1  = 0;
+
+        int left2 = 200;
+        int top2  = 0;
+
+        g.setColor(Color.white);
+        g.drawString("energy:", 5, top1 + 15);
+        g.drawString("damage:", 5, top1 + 30);
+
+        if (getPilot1() != null)
+            paintLevels(g, getPilot1().getShip(), top1, left1);
+        if (getPilot2() != null)
+            paintLevels(g, getPilot2().getShip(), top2, left2);
+    }
+
+    void paintLevels(Graphics g, Ship ship, int top, int left) {
+        if (ship == null)
+            return;
+        else if (ship.isAlive()) {
+            g.drawString(Float.toString(ship.getEnergyLevel()*100), left+1, top+15);
+            g.drawString(Float.toString(ship.getDamageLevel()*100), left+1, top+30);
+        }
+        else {
+            g.setColor(Color.red);
+            g.drawString("Ship is destroyed", left+1, top+15);
+        }
+    }
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/spacewar/EnergyPacket.java b/tests/multiIncremental/SpaceWar/base/src/spacewar/EnergyPacket.java
new file mode 100644 (file)
index 0000000..d7bc4f9
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+
+EnergyPacket.java
+Part of the Spacewar system.
+
+*/
+
+package spacewar;
+
+
+class EnergyPacket extends SpaceObject {
+
+  static private final int SIZE = 5;             //Can't be changed for now!!!
+  int getSize() { return SIZE; }
+
+  private double energy;
+
+  double getEnergy() { return energy; }
+
+  EnergyPacket(Game theGame,
+               double xP, double yP, double xV, double yV, double e) {
+    super(theGame, xP, yP, xV, yV);
+    energy = e;
+  }
+
+  void handleCollision(SpaceObject obj) {
+    die();
+  }
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/spacewar/EnergyPacketProducer.java b/tests/multiIncremental/SpaceWar/base/src/spacewar/EnergyPacketProducer.java
new file mode 100644 (file)
index 0000000..efd2760
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+
+EnergyPacketProducer.java
+Part of the Spacewar system.
+
+ This implementation creates booby-trapped packets 20% of the time.
+
+*/
+
+package spacewar;
+
+
+class EnergyPacketProducer extends Thread {
+    private final static int MIN = -20;
+    private final static int MAX = 80;
+    private final static int EXPECTEDINTERVAL = 15;
+
+    private Game game;
+
+    Game getGame() { return game; }
+
+    EnergyPacketProducer(Game theGame) {
+        super("EnergyPacketProducer");
+        game = theGame;
+    }
+
+    public void run() {
+        while(true) {
+            produceAPacket();
+            waitForABit();
+        }
+    }
+
+    void waitForABit() {
+        try { Thread.sleep((int)(Math.random() * EXPECTEDINTERVAL * 2000)); }
+        catch (InterruptedException e) {}
+    }
+
+    void produceAPacket() {
+        EnergyPacket pkt =
+            new EnergyPacket(game,
+                             Math.random() * getGame().getWidth(),
+                             Math.random() * getGame().getHeight(),
+                             Math.random() * 2 - 1,
+                             Math.random() * 2 - 1,
+                             Math.random() * (MAX - MIN) + MIN);
+    }
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/spacewar/EnsureShipIsAlive.java b/tests/multiIncremental/SpaceWar/base/src/spacewar/EnsureShipIsAlive.java
new file mode 100644 (file)
index 0000000..f7b949a
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+Ship.java
+Part of the Spacewar system.
+
+*/
+
+package spacewar;
+
+/**
+ * This aspect makes sure that the ship is alive before performing any console
+ * commands.
+ *
+ */
+aspect EnsureShipIsAlive {
+    void around (Ship ship): Ship.helmCommandsCut(ship) {
+        if ( ship.isAlive() ) {
+            proceed(ship);
+        }
+    }
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/spacewar/Game.java b/tests/multiIncremental/SpaceWar/base/src/spacewar/Game.java
new file mode 100644 (file)
index 0000000..da67d7b
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+
+Game.java
+Part of the Spacewar system.
+
+*/
+
+package spacewar;
+
+import java.awt.Dimension;
+
+/**
+ * The Game class is the root of the spacewar game.  To start a spacewar
+ * game, you can either call the main method, or instantiate this class
+ * directly.
+ *
+ * Synchronization is done by the GameSynchronization aspect.
+ */
+public class Game extends Thread {
+
+    /**
+     * To run the game from top level, simply say Java Game, as usual.  Passing
+     * an argument makes the game run in demo mode.  Without an argument it runs
+     * in the normal player mode.
+     */
+    public static void main(String[] args) {
+        if ( args.length == 0 )
+            new Game("1").run();
+        new Game(args[0]).run();
+    }
+
+
+    private Timer    timer;
+    private EnergyPacketProducer ePP;
+
+    private Registry registry;
+    private Pilot    pilot1, pilot2;
+
+    private Dimension screenSize = new Dimension(500, 500);
+
+    Registry getRegistry() { return registry; }
+    Pilot    getPilot1()   { return pilot1; }
+    Pilot    getPilot2()   { return pilot2; }
+
+    /** returns the width of the screen, delegating to screenSize */
+    int getWidth()  { return screenSize.width;  }
+
+    /** returns the height of the screen, delegating to screenSize */
+    int getHeight() { return screenSize.height; }
+
+    /**
+     * To run the game, simply instantiate this class.  It runs in its own
+     * thread.  You can instantiate multiple games at once.  For the time being
+     * the only way to end the game is to exit from the Java VM.
+     *
+     * @param isDemo Controls whether the game runs in demo mode or not.  True
+     *   means it is a demo, false means it runs in normal 2 player mode.
+     */
+    public Game(String mode) {
+        timer    = new Timer(this);
+        ePP      = new EnergyPacketProducer(this);
+        registry = new Registry(this);
+    }
+
+    public void run() {
+        timer.start();
+        ePP.start();
+
+        while(true) {
+            try {
+                newRobot(3);
+                Thread.sleep(15000);
+            }
+            catch (InterruptedException e) {}
+        }
+    }
+
+
+    /**
+     * add a robot to the game.  This is a menu command.
+     */
+    void addRobot() {
+        newRobot(3);
+    }
+
+    /**
+     * resurrect the ships in the game.  This is a menu command.
+     */
+    void resetShips() {
+        Ship[] ships = registry.getShips();
+
+        for (int i = 0; i < ships.length; i++) {
+            Ship ship = ships[i];
+            Pilot pilot = ship.getPilot();
+            newShip(pilot);
+        }
+    }
+
+    /**
+     * leave the game.  This is a menu command.
+     */
+    void quit() {
+        System.exit(0);
+    }
+
+    void error(Object o) {
+        System.err.println(o);
+    }
+
+
+    /**
+     * returns a new player.  With {@link #newRobot} and {@link
+     * #newShip}, the only ways to make a Player, a Robot, or a Ship.
+     * The structural invariant is that there should be no calls to
+     * new of one of these three classes outside these three methods.
+     */
+    Player newPlayer(int number) {
+        Player player = new Player(this, number);
+        newShip(player);
+        return player;
+    }
+
+    /**
+     * returns a new robot.  With {@link #newPlayer} and {@link
+     * #newShip}, the only ways to make a Player, a Robot, or a Ship.
+     * The structural invariant is that there should be no calls to
+     * new of one of these three classes outside these three methods.
+     */
+    Robot newRobot(int number) {
+        Robot robot = new Robot(this, number);
+        newShip(robot);
+        robot.start();
+        return robot;
+    }
+
+    /**
+     * returns a new ship.  With {@link #newRobot} and {@link
+     * #newPlayer}, the only ways to make a Player, a Robot, or a
+     * Ship.  The structural invariant is that there should be no
+     * calls to new of one of these three classes outside these three
+     * methods.
+     */
+    Ship newShip(Pilot pilot) {
+        //
+        // If there is an old ship (we're doing a reset), then remove it from
+        // the registry.
+        //
+        Ship oldShip = pilot.getShip();
+        if (! (oldShip == null))
+            oldShip.die();
+
+        Ship newShip = new Ship(this,
+                                Math.random() * getWidth(),
+                                Math.random() * getHeight(),
+                                Math.random() * Math.PI * 2);
+        pilot.setShip(newShip);
+        newShip.setPilot(pilot);
+
+        return newShip;
+    }
+
+    void clockTick() {
+        registry.clockTick();
+        handleCollisions();
+    }
+
+    // collision detection
+
+    void handleCollisions() {
+        SpaceObject[] objects = registry.getObjects();
+
+        SpaceObject objI, objJ;
+        for (int i = 0; i < objects.length; i++) {
+            objI = objects[i];
+            for (int j = i + 1; j < objects.length; j++) {
+                objJ = objects[j];
+                if (objI instanceof Bullet && objJ instanceof Bullet)
+                    continue;
+                if (isCollision(objI, objJ)) {
+                    if (objI instanceof Ship && objJ instanceof Ship)
+                        Ship.bounce((Ship)(objI), (Ship)(objJ));
+                    else {
+                        objI.handleCollision(objJ);
+                        objJ.handleCollision(objI);
+                    }
+                }
+            }
+        }
+    }
+
+    /*
+     * Is the distance between the two centers less than the sum of the two
+     * radii.  This is a cheap and dirty (i.e. wrong) implementation of this.
+     */
+    static boolean isCollision(SpaceObject a, SpaceObject b) {
+        return (Math.abs(a.getXPos() - b.getXPos()) +
+                Math.abs(a.getYPos() - b.getYPos())) <
+            (a.getSize()/2 + b.getSize()/2);
+    }
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/spacewar/GameSynchronization.java b/tests/multiIncremental/SpaceWar/base/src/spacewar/GameSynchronization.java
new file mode 100644 (file)
index 0000000..dcf42e9
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+
+RegistrySynchronization.java
+Part of the Spacewar system.
+
+*/
+
+package spacewar;
+
+import coordination.Coordinator;
+
+/**
+ * This aspect ensures synchronized access to methods of the Game in the
+ * presence of several threads.
+ *
+ * It uses the Coordinator class, from the AspectJ coordination library.
+ * (This case is right on the borderline of being too simple to use the
+ * coordination library, but we use it anyways to keep the similarity
+ * with the RegistrySynchronizer.)
+ *
+ * It uses a per-Game coordination scheme, so there is one instance of
+ * this class for each instance of the Game class.  When this class is
+ * constructed, it registers appropriate mutexes and selfexes using
+ * the behavior inherited from Coordinator.
+ *
+ * The coordination constraints for the Game are simple.  We just need to
+ * make sure that newShip and handleCollisions are mutually exclusive.  That
+ * ensures that they we can't destroy a ship that has just been replaced.
+ */
+aspect GameSynchronization extends Coordinator perthis(this(Game)) {
+
+    protected pointcut synchronizationPoint():
+       call(void Game.handleCollisions(..)) || call(Ship Game.newShip(..));
+
+    public GameSynchronization() {
+        addMutex(new String[] {"handleCollisions", "newShip"});
+    }
+
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/spacewar/Pilot.java b/tests/multiIncremental/SpaceWar/base/src/spacewar/Pilot.java
new file mode 100644 (file)
index 0000000..330d860
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+
+*/
+
+package spacewar;
+
+
+/**
+ * Pilot is the abstract superclass of Player and Robot.
+ *
+ */
+
+abstract class Pilot {
+    private Game game;
+    private int  number;
+    protected Ship ship = null;
+
+    Game getGame()   { return game; }
+    int  getNumber() { return number; }
+    Ship getShip()   { return ship; }
+
+    void setShip(Ship s) { ship = s; }
+
+    Pilot (Game g, int n) {
+        super();
+        game   = g;
+        number = n;
+    }
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/spacewar/Player.java b/tests/multiIncremental/SpaceWar/base/src/spacewar/Player.java
new file mode 100644 (file)
index 0000000..3899c6d
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+
+*/
+
+package spacewar;
+
+import java.awt.event.KeyListener;
+import java.awt.event.KeyEvent;
+
+class Player extends Pilot implements KeyListener {
+
+    private KeyMapping keyMapping;
+
+    /** current rotation key */
+    private int        rotation_direction = Ship.STOP; // current rotation key
+
+    /** current thrust */
+    private boolean    thrust_on = false;
+
+    Player(Game theGame, int number) {
+        super(theGame,number);
+
+        if (getNumber() == 1)
+            keyMapping = KeyMapping.keyMapping1;
+        else if (getNumber() == 2)
+            keyMapping = KeyMapping.keyMapping2;
+
+    }
+
+    public void keyPressed(KeyEvent e) {
+        int keyCode = e.getKeyCode();
+        boolean consumed = true;
+
+        if (keyCode == keyMapping.fire) {
+            ship.fire();
+        }
+        else if (keyCode == keyMapping.thrust && !thrust_on) {
+            ship.thrust(true);
+            thrust_on = true;
+        }
+        else if (keyCode == keyMapping.right &&
+                 rotation_direction != Ship.COUNTERCLOCKWISE) {
+            //start rotating clockwise unless already rotating in the
+            //opposite direction
+            rotation_direction = Ship.CLOCKWISE;
+            ship.rotate(Ship.CLOCKWISE);
+        }
+        else if (keyCode == keyMapping.left &&
+                 rotation_direction != Ship.CLOCKWISE) {
+            //start rotating counterclockwise unless already rotating in the
+            //opposite direction
+            rotation_direction = Ship.COUNTERCLOCKWISE;
+            ship.rotate(Ship.COUNTERCLOCKWISE);
+        }
+        else {
+            consumed = false;
+        }
+
+        if (consumed) e.consume();
+    }
+
+    public void keyReleased(KeyEvent e) {
+        int keyCode = e.getKeyCode();
+
+        if (keyCode == keyMapping.thrust) {
+            ship.thrust(false);                 //engine off
+            thrust_on = false;
+        }
+        else if (keyCode == keyMapping.right &&
+                 rotation_direction == Ship.CLOCKWISE
+                 ||
+                 keyCode == keyMapping.left &&
+                 rotation_direction == Ship.COUNTERCLOCKWISE) {
+            ship.rotate(Ship.STOP);             //stop rotation
+            rotation_direction = Ship.STOP;
+        }
+    }
+
+    public void keyTyped(KeyEvent e) {
+        // have to implement this because it's in KeyListener
+    }
+}
+
+class KeyMapping {
+
+    static final KeyMapping keyMapping1 =
+        new KeyMapping(KeyEvent.VK_LEFT,
+                       KeyEvent.VK_RIGHT,
+                       KeyEvent.VK_UP,
+                       KeyEvent.VK_SPACE);
+
+    static final KeyMapping keyMapping2 =
+        new KeyMapping(KeyEvent.VK_X,
+                       KeyEvent.VK_V,
+                       KeyEvent.VK_D,
+                       KeyEvent.VK_ALT);
+
+    int left, right, thrust, fire;
+
+    KeyMapping(int k_left, int k_right, int k_thrust, int k_fire) {
+        left   = k_left;
+        right  = k_right;
+        thrust = k_thrust;
+        fire   = k_fire;
+    }
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/spacewar/Registry.java b/tests/multiIncremental/SpaceWar/base/src/spacewar/Registry.java
new file mode 100644 (file)
index 0000000..a9cec04
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+
+Registry.java
+Part of the Spacewar system.
+
+*/
+
+package spacewar;
+
+import java.util.Vector;
+import java.util.Hashtable;
+import java.util.Enumeration;
+
+/**
+ * The Registry keeps track of all the space objects that are floating around.
+ * It basically supports register, unregister and contents type operations.
+ *
+ * The synchronization is done by the RegistrySynchronization aspect.
+ */
+
+class Registry {
+
+    private Hashtable table;
+    private Game      game;
+
+    Game getGame() { return game; }
+
+    Registry (Game theGame) {
+        game = theGame;
+        table = new Hashtable();
+    }
+
+
+    void register(SpaceObject object) {
+        table.put(object, object);
+    }
+
+    void unregister(SpaceObject object) {
+        table.remove(object);
+    }
+
+    /*
+     * It is an invariant of the design that only two points in SpaceObject
+     * should call register and unregister.  This aspect enforces that.
+     *
+     * Unfortunately, in the current compiler, we get a static warning when
+     * there are no illegal calls that this advice has no targets.  That will
+     * be fixed in a future release.  For the time being the dummy method
+     * just below this fixes that.
+     */
+    static aspect RegistrationProtection {
+        after() returning():
+            (call(void Registry.register(SpaceObject)) ||
+             call(void Registry.unregister(SpaceObject))) &&
+            !(within(SpaceObject) && (withincode(new(..)) ||
+                                      withincode(void die()))) {
+            throw new IllegalAccessError(
+               "This is an illegal call to " + thisJoinPoint + "\n" +
+               "Only the constructor and the die() on SpaceObject\n" +
+               "should call the primitive registry operations.");
+        }
+    }
+
+    void dummy() {        // see comment above
+        register(getObjects()[0]);
+        unregister(getObjects()[0]);
+    }
+
+
+    SpaceObject[] getObjects() {
+        SpaceObject[] allObjects = new SpaceObject[table.size()];
+        Enumeration elements = table.elements();
+        for(int i = 0; elements.hasMoreElements(); i++) {
+            allObjects[i] = (SpaceObject)(elements.nextElement());
+        }
+        return allObjects;
+    }
+
+    Ship[] getShips() {
+        //
+        // First we have to put just the Ships into a vector, then we can put
+        // them into an array of exactly the right length.
+        //
+        Ship[]  arrayOfShips;
+        Vector vectorOfShips = new Vector();
+        Enumeration elements = table.elements();
+        while (elements.hasMoreElements()) {
+            Object object = elements.nextElement();
+            if (object instanceof Ship) {
+                vectorOfShips.addElement(object);
+            }
+        }
+
+        arrayOfShips = new Ship[(vectorOfShips.size())];
+        vectorOfShips.copyInto(arrayOfShips);
+        return arrayOfShips;
+    }
+
+    Hashtable getTable() { return table; }
+
+    //
+    // The protocol for clockTick is that it automatically cascades.
+    //
+    void clockTick() {
+        Enumeration elements = table.elements();
+        while (elements.hasMoreElements()) {
+            ((SpaceObject)elements.nextElement()).clockTick();
+        }
+    }
+}
+
diff --git a/tests/multiIncremental/SpaceWar/base/src/spacewar/RegistrySynchronization.java b/tests/multiIncremental/SpaceWar/base/src/spacewar/RegistrySynchronization.java
new file mode 100644 (file)
index 0000000..986e4bd
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+
+RegistrySynchronization.java
+Part of the Spacewar system.
+
+*/
+
+package spacewar;
+
+import coordination.Coordinator;
+
+
+/**
+ * This aspect ensures synchronized access to methods of the Registry in
+ * the presence of several threads.
+ *
+ * It uses the Coordinator class, from the AspectJ coordination library.
+ *
+ * It uses a per-Registry coordination scheme, so there is one instance of
+ * this class for each instance of the Registry class.  When this class is
+ * constructed, it registers appropriate mutexes and selfexes using the
+ * behavior inherited from Coordinator.
+ *
+ * The mutating methods (register and unregister) should be self-exclusive.
+ * Each reader method should be mutually exclusive with the mutating
+ * methods.  But the readers can run concurrently.  */
+aspect RegistrySynchronization extends Coordinator perthis(this(Registry)) {
+
+    protected pointcut synchronizationPoint():
+        call(void Registry.register(..))   ||
+        call(void Registry.unregister(..)) ||
+        call(SpaceObject[] Registry.getObjects(..)) ||
+        call(Ship[] Registry.getShips(..));
+    
+    public RegistrySynchronization() {
+        addSelfex("register");
+        addSelfex("unregister");
+
+        addMutex(new String[] {"register", "unregister", "getObjects"});
+        addMutex(new String[] {"register", "unregister", "getShips"});
+    }
+
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/spacewar/Robot.java b/tests/multiIncremental/SpaceWar/base/src/spacewar/Robot.java
new file mode 100644 (file)
index 0000000..05f2761
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+
+Robot.java
+Part of the Spacewar system.
+
+*/
+
+package spacewar;
+
+import java.util.Random;
+
+/**
+ * Robot is an automatic pilot that now has quite a bit of intelligence.
+ * So, beware !
+ */
+class Robot extends Pilot implements Runnable {
+
+    private static final int FIRE_INTERVAL  = 60;
+    private static final int REBIRTH_DELAY  = 900;
+
+    private final Random random = new Random();
+
+    private Thread  runner;
+    private boolean runnable = true;
+
+    Robot(Game theGame, int number) {
+        super(theGame, number);
+    }
+
+    void start() {
+        if (runner == null) {
+            runner = new Thread(this);
+            runner.start();
+        }
+    }
+
+    void destroy() {
+        if (runner != null) {
+            runnable = false;
+            runner   = null;
+        }
+    }
+
+
+    // A Robot tracks User-controlled ships and fires at them
+    public void run() {
+        Ship target = null;
+
+        while(runnable) {
+            // find target ship
+            do {
+                Ship[] potentials = getGame().getRegistry().getShips();
+                if(potentials.length != 0)
+                    target = potentials[Math.abs(random.nextInt() % potentials.length)];
+                sleepForABit(25);
+            } while (target == ship);
+            // main loop
+            int     currentRotation       = Ship.STOP;
+            int     time;
+            boolean currentlyAccelerating = false;
+            double  dx, dy, angleA, angleB, theta, dtheta, d,
+                targetVel, a, b, c, targetXVel, targetYVel;
+
+            while(true) {
+                sleepForABit(FIRE_INTERVAL);
+
+                // if my ship is destroyed, give me a new one
+                if (!ship.isAlive()) {
+                    sleepForABit(REBIRTH_DELAY);
+                    getGame().newShip(this);
+                }
+
+                // find direction and distance from target to me
+                dx = ship.getXPos() - target.getXPos();
+                if (dx < - getGame().getWidth() / 2)
+                    dx += getGame().getWidth();
+                if (dx > getGame().getWidth() / 2)
+                    dx -= getGame().getWidth();
+                dy = ship.getYPos() - target.getYPos();
+                if (dy < - getGame().getHeight() / 2)
+                    dy += getGame().getHeight();
+                if (dy > getGame().getHeight() / 2)
+                    dy -= getGame().getHeight();
+                d = Math.sqrt(dx * dx + dy * dy);
+                angleA = Math.atan(dy / dx);
+                if (dx < 0)
+                    angleA += Math.PI;
+
+                // find relative velocity and trajectory of target
+                targetXVel = target.getXVel() - ship.getXVel();
+                targetYVel = target.getYVel() - ship.getYVel();
+                targetVel = Math.sqrt(targetXVel * targetXVel +
+                                      targetYVel * targetYVel);
+                angleB = Math.atan(targetYVel / targetXVel);
+                if (targetXVel < 0)
+                    angleB+=Math.PI;
+
+                // find angle between line to target and taget's direction of travel
+                theta = (angleA - angleB) % (2 * Math.PI);
+                if (theta < -Math.PI)
+                    theta += 2 * Math.PI;
+                if (theta > Math.PI)
+                    theta -= 2 * Math.PI;
+
+                // calculate time to bullet impact using law of cosines
+                a = targetVel * targetVel + Ship.BULLET_SPEED * Ship.BULLET_SPEED;
+                b = d * targetVel * Math.cos(theta);
+                c = - d * d;
+                time = (int)((-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a);
+
+                // calculate angle and distance to bullet impact location
+                dx = targetXVel * time - dx;
+                dy = targetYVel * time - dy;
+                theta = Math.atan(dy / dx);
+                if(dx < 0)
+                    theta += Math.PI;
+
+                // find desired change in rotation
+                dtheta = (theta - ship.getOrientation()) % (2 * Math.PI);
+                // find the shortest path to the desired orientation;
+                if(dtheta < - Math.PI)
+                    dtheta += 2 * Math.PI;
+                if(dtheta > Math.PI)
+                    dtheta -= 2 * Math.PI;
+
+                // turn if nessecary
+                if (dtheta > Ship.DEFAULT_ANGULAR_VELOCITY / 2) {
+                    if (currentRotation != Ship.CLOCKWISE)
+                        ship.rotate(currentRotation = Ship.CLOCKWISE);
+                }
+                else if (dtheta < -Ship.DEFAULT_ANGULAR_VELOCITY / 2) {
+                    if (currentRotation != Ship.COUNTERCLOCKWISE)
+                        ship.rotate(currentRotation = Ship.COUNTERCLOCKWISE);
+                } // otherwise, fire, maybe even a burst
+                else {
+                    if(currentRotation != Ship.STOP)
+                        ship.rotate(currentRotation = Ship.STOP);
+                    if (random.nextInt() % 40 == 0) {
+                        ship.fire();
+                    }
+                }
+
+                // randomly accelerate
+                if (currentlyAccelerating && random.nextInt() % 2 == 0)
+                    ship.thrust(currentlyAccelerating = false);
+                else {
+                    if (ship.getXVel() == 0)
+                        angleA = 0;
+                    else
+                        angleA = Math.atan(ship.getYVel() / ship.getXVel());
+
+                    if (ship.getXVel() < 0)
+                        angleA+=Math.PI;
+                    angleB = (angleA - ship.getOrientation()) % (2 * Math.PI);
+                    if (angleB < -Math.PI)
+                        angleB += 2 * Math.PI;
+                    if (angleB > Math.PI)
+                        angleB -= 2 * Math.PI;
+                    angleB = Math.abs(angleB);
+
+                    // angleB now represents the angle between the ship's
+                    // orientation and velocity vector.  This will be used to
+                    // determine the probably that the ship will thrust to
+                    // prevent ships from accelerating too much in one direction
+                    if (random.nextInt() % (int)(12 * (Math.PI - angleB) + 1) == 0)
+                        ship.thrust(currentlyAccelerating = true);
+                }
+
+                // switch targets if current one has been destroyed
+                if (target.getDamage() == 100)
+                    break;
+
+                // randomly switch targets
+                if (random.nextInt() % 4000 == 0)
+                    break;
+            }
+        }
+    }
+
+    void sleepForABit (int time) {
+        try {
+            runner.sleep(time);
+        }
+        catch (InterruptedException e) {}
+    }
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/spacewar/SWFrame.java b/tests/multiIncremental/SpaceWar/base/src/spacewar/SWFrame.java
new file mode 100644 (file)
index 0000000..6dfb9f6
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+
+SWFrame.java
+Part of the Spacewar system.
+
+*/
+
+package spacewar;
+
+import java.awt.Frame;
+import java.awt.Menu;
+import java.awt.MenuBar;
+import java.awt.MenuItem;
+import java.awt.MenuShortcut;
+import java.awt.Dimension;
+import java.awt.Insets;
+
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+
+class SWFrame extends Frame implements ActionListener {
+    private Game     game;
+    private Display  display;
+    private Menu     menu;
+
+    Game     getGame()      { return game; }
+    Display  getDisplay() { return display; }
+    Menu     getMenu()      { return menu; }
+
+    SWFrame(Game theGame, Display d) {
+        super("Space War!");
+
+        game = theGame;
+
+        display = d;
+        add(display);
+
+        // create menu
+        menu = new Menu("Game");
+        MenuItem item1 = new MenuItem("Add Robot",   new MenuShortcut('a'));
+        MenuItem item2 = new MenuItem("Reset Ships", new MenuShortcut('r'));
+        MenuItem item3 = new MenuItem("Quit",        new MenuShortcut('q'));
+        item1.setActionCommand("Add Robot");
+        item2.setActionCommand("Reset Ships");
+        item3.setActionCommand("Quit");
+        menu.add(item1);
+        menu.add(item2);
+        menu.add(item3);
+        menu.addActionListener(this);
+
+        setMenuBar(new MenuBar());
+        getMenuBar().add(menu);
+
+        Dimension screenSize = new Dimension(500, 500);
+        setSize(screenSize);
+        setVisible(true);
+        toFront();
+
+        Insets inset = getInsets();
+        int displayWidth  = screenSize.width - inset.left - inset.right;
+        int displayHeight = screenSize.height - inset.top - inset.bottom;
+        display.setSize(displayWidth, displayHeight);
+    }
+
+    public void actionPerformed(ActionEvent e) {
+        String s = e.getActionCommand();
+        if (s.equals("Add Robot")) {
+            getGame().addRobot();
+        }
+        else if (s.equals("Reset Ships")) {
+            getGame().resetShips();
+        }
+        else if (s.equals("Quit")) {
+            getGame().quit();
+        }
+    }
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/spacewar/Ship.java b/tests/multiIncremental/SpaceWar/base/src/spacewar/Ship.java
new file mode 100644 (file)
index 0000000..9978fe6
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+Ship.java
+Part of the Spacewar system.
+
+*/
+
+package spacewar;
+
+class Ship extends SpaceObject {
+
+    pointcut helmCommandsCut(Ship ship):
+        target(ship) && ( call(void rotate(int))     ||
+                         call(void thrust(boolean)) ||
+                         call(void fire()) );
+
+
+    /**
+     * Energy and Damage are key values in the state of a ship.  Energy is
+     * basically about fuel, and damage is about how bad a shape we are in.
+     *
+     * The energy related values are:
+     * <ul>
+     *   <li>MAX_ENERGY</li>
+     *   <li>BULLET_ENERGY</li>
+     *   <li>ACCELERATION_ENERGY_FACTOR</li>
+     *   <li>energy</li>
+     * </ul>
+     * The damage related values are:
+     * <ul>
+     *   <li>MAX_DAMAGE</li>
+     *   <li>BULLET_DAMAGE</li>
+     *   <li>COLLISION_DAMAGE_FACTOR</li>
+     *   <li>damage</li>
+     * </ul>
+     * Finally, REPAIR_RATE is the rate at which energy is consumed to fix
+     * damage.
+     *
+     */
+    private static final int    MAX_ENERGY = 100;
+    private static final int    BULLET_ENERGY= 2;
+    private static final double ACCELERATION_COST_FACTOR = 0.05;
+
+    //XXX was private
+    static final int    MAX_DAMAGE = 100;
+    private static final int    BULLET_DAMAGE = 15;
+    private static final double COLLISION_DAMAGE_FACTOR = 0.1;
+
+    private static final double REPAIR_RATE = 0.08;
+
+
+    private static final int    EXPLOSION_LENGTH = 10;
+
+    static final int    BULLET_SPEED = 10;
+
+    static final int    CLOCKWISE = 1;
+    static final int    STOP = 0;
+    static final int    COUNTERCLOCKWISE = (-1);
+
+    static final double DEFAULT_ANGULAR_VELOCITY = 0.2;
+    static final double DEFAULT_ACCELERATION = .4;
+
+    static private final int SIZE = 30;     //Can't be changed for now!!!
+
+    private double    energy;               // range: 0 to MAX_ENERGY
+    private double    damage;               // range: 0 to MAX_DAMAGE
+    private double    orientation;          // in degrees
+    private double    angularVel;           // in ???
+    private double    xAcc, yAcc, rAcc;     //
+    private int       countdown;            // remaining explosion time
+
+    private Pilot     pilot;
+
+    Ship(Game theGame, double xPos, double yPos, double orientation) {
+        super(theGame, xPos, yPos, 0, 0);
+        xAcc = 0;
+        yAcc = 0;
+        this.orientation = orientation;
+        angularVel = 0;
+
+        energy = MAX_ENERGY;
+        damage = 0;
+        countdown = EXPLOSION_LENGTH;
+    }
+
+
+    int getSize()            { return SIZE; }
+
+    double getEnergy()       { return energy; }
+    double getDamage()       { return damage; }
+    double getOrientation()  { return orientation; }
+    double getRAcc()         { return rAcc; }
+
+    Pilot getPilot()         { return pilot; }
+    void  setPilot (Pilot p) { pilot = p; }
+
+    float getEnergyLevel() {
+        return (float)energy / (float)MAX_ENERGY;
+    }
+    float getDamageLevel() {
+        return (float)damage / (float)MAX_DAMAGE;
+    }
+
+    /** returns false if energy is out, otherwise decrements energy by amount
+     * and returns true
+     */
+    boolean expendEnergy(double amount) {
+        if (amount <= energy) {
+            energy -= amount;
+            return true;
+        }
+        else
+            return false;
+    }
+
+    /** increments damage by amount and handles the destruction of a ship if
+     * damage reaches MAX_DAMAGE.
+     */
+    void inflictDamage(double amount) {
+        if (amount < 0)     // shouldn't happen
+            return;
+        damage = Math.min(MAX_DAMAGE, damage + amount);
+        if (damage == MAX_DAMAGE)
+            setIsAlive(false);
+    }
+
+    /** repairs some damage
+     */
+    void repairDamage(double amount) {
+        if (amount < 0)     // shouldn't happen
+            return;
+        if (damage == 0)
+            return;
+        damage = Math.max(0, damage - amount);
+    }
+
+    public void clockTick() {
+        if (! isAlive())  {
+            //
+            // If we aren't alive, but we are still in the registry, it means
+            // we are exploding.  countdown counts the length of the explosion.
+            //
+            if (--countdown == 0)
+                die();
+        }
+        else {
+            if (angularVel != 0) {
+                orientation += angularVel;
+                xAcc = rAcc * Math.cos(orientation);
+                yAcc = rAcc * Math.sin(orientation);
+            }
+            setXVel(getXVel() + xAcc);
+            setYVel(getYVel() + yAcc);
+
+            //expend energy
+            if (!expendEnergy(rAcc * ACCELERATION_COST_FACTOR))
+                rAcc = xAcc = yAcc = 0;
+
+            // fix damage
+            if (energy > 10 && damage > REPAIR_RATE) {
+                expendEnergy(REPAIR_RATE);
+                repairDamage(REPAIR_RATE);
+            }
+        }
+        super.clockTick();
+    }
+
+    /**
+     * First check to make sure we have enough energy to accelerate.  If
+     * we do, then go ahead and do so.  Acceleration is in the direction
+     * we are already facing (i.e. orientation).
+     */
+    void setAcceleration(double acc) {
+        if (acc * ACCELERATION_COST_FACTOR <= energy) {
+            rAcc = acc;
+            xAcc = rAcc * Math.cos(orientation);
+            yAcc = rAcc * Math.sin(orientation);
+        }
+    }
+
+    /**
+     * First check to make sure we have enough energy to rotate.  If
+     * we do, then go ahead and do so.
+     */
+    void setAngularVelocity(double omega) {
+        // changing direction of rotation takes energy
+        if (!expendEnergy(Math.abs(omega - angularVel) / 2))
+            return;
+        //sets amount of degree rotation per clock tick, in radians;
+        //clockwise is positive
+        angularVel = omega;
+    }
+
+    /** affect rotation thrusters.  Direction can be one of {@link
+     * #CLOCKWISE}, {@link #COUNTERCLOCKWISE}, or zero for turning off
+     * the thrusters.
+     */
+    void rotate(int direction) {
+        setAngularVelocity(
+          direction == CLOCKWISE        ? DEFAULT_ANGULAR_VELOCITY :
+          direction == COUNTERCLOCKWISE ? -DEFAULT_ANGULAR_VELOCITY :
+          0);
+    }
+
+    /** turn on acceleration */
+    void thrust(boolean onOff) {
+        setAcceleration(onOff ? DEFAULT_ACCELERATION : 0);
+    }
+
+    /** create a bullet and fire it */
+    void fire() {
+        // firing a shot takes energy
+        if (!expendEnergy(BULLET_ENERGY))
+            return;
+
+        //create a bullet object so it doesn't hit the ship that's firing it
+        double xV = getXVel() + BULLET_SPEED * (Math.cos(orientation));
+        double yV = getYVel() + BULLET_SPEED * (Math.sin(orientation));
+
+        // create the actual bullet
+        new Bullet(
+         getGame(),
+         (getXPos() + ((getSize()/2 + 2) * (Math.cos(orientation))) + xV),
+         (getYPos() + ((getSize()/2 + 2) * (Math.sin(orientation))) + yV),
+         xV,
+         yV);
+    }
+
+
+    void handleCollision(SpaceObject obj) {
+        if (obj instanceof Ship) {
+            // should never be called. ship - ship collisions are handled in
+            // Ship.bounce(Ship shipA, Ship shipB)
+        }
+        else if (obj instanceof Bullet) {
+            inflictDamage(BULLET_DAMAGE);
+        }
+        else if (obj instanceof EnergyPacket) {
+            double packetEnergy = ((EnergyPacket)obj).getEnergy();
+            energy = Math.max(0, Math.min(energy + packetEnergy, MAX_ENERGY));
+        }
+        else {
+            System.err.println("collision with UFO!");
+        }
+    }
+
+    static void bounce(Ship shipA, Ship shipB) {
+        double  dx, dy, denominator,
+            xAccA, yAccA, xAccB, yAccB, damage,
+            xComp, yComp, dvx, dvy;
+
+        dx = Math.abs(shipA.getXPos() - shipB.getXPos());
+        dy = Math.abs(shipA.getYPos() - shipB.getYPos());
+        denominator = Math.sqrt(dx * dx + dy * dy);
+        xComp = dx / denominator;
+        yComp = dy / denominator;
+        xAccA = shipB.getXVel() * xComp + shipA.getXVel() * (1 - xComp) -
+            shipA.getXVel();
+        yAccA = shipB.getYVel() * yComp + shipA.getYVel() * (1 - yComp) -
+            shipA.getYVel();
+        xAccB = shipA.getXVel() * xComp + shipB.getXVel() * (1 - xComp) -
+            shipB.getXVel();
+        yAccB = shipA.getYVel() * yComp + shipB.getYVel() * (1 - yComp) -
+            shipB.getYVel();
+        shipA.accelerate(xAccA, yAccA);
+        shipB.accelerate(xAccB, yAccB);
+        dvx = shipA.getXVel() - shipB.getXVel();
+        dvy = shipA.getYVel() - shipA.getYVel();
+        damage = COLLISION_DAMAGE_FACTOR * (dvx * dvx + dvy * dvy);
+        shipA.inflictDamage(damage);
+        shipB.inflictDamage(damage);
+
+        // !!!
+        // !!! poopers!  this does a local time warp.  this has to be a
+        // !!! violation of the clockTick protocol
+        // !!!
+        while (Game.isCollision(shipA, shipB)) {
+            shipA.clockTick();
+            shipB.clockTick();
+        }
+    }
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/spacewar/SpaceObject.java b/tests/multiIncremental/SpaceWar/base/src/spacewar/SpaceObject.java
new file mode 100644 (file)
index 0000000..ee3afab
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+
+SpaceObject.java
+Part of the Spacewar system.
+
+*/
+
+package spacewar;
+
+
+/**
+ * SpaceObjects are objects that float around in space.  They support the
+ * minimal SpaceObject protocol, having to do with position, velocity,
+ * size and liveness.  They are constructed with game, position, velocity
+ * and size.  When constructed, a spaceobject adds itself to the registry.
+ *
+ * When it dies, a spaceobject removes itself from the registry.  But note
+ * that it doesn't decide when to die, subclasses do that.
+ *
+ * The display aspects actually draw the space object on the screen and say
+ * how much space it takes up there.
+ */
+abstract class SpaceObject {
+
+    private Game    game;
+    private double  xPos, yPos, oldXPos, oldYPos, xVel, yVel;
+    private boolean alive;
+
+    SpaceObject (Game theGame, double xP, double yP, double xV, double yV) {
+        game = theGame;
+        xPos = xP;
+        yPos = yP;
+        oldXPos = xP;
+        oldYPos = yP;
+        xVel = xV;
+        yVel = yV;
+
+        alive = true;
+        getGame().getRegistry().register(this);
+    }
+
+    Game getGame()      { return game; }
+
+    double getXPos()    { return xPos; }
+    double getYPos()    { return yPos; }
+
+    double getOldXPos() { return oldXPos; }
+    double getOldYPos() { return oldYPos; }
+
+    double getXVel()    { return xVel; }
+    double getYVel()    { return yVel; }
+
+    void setXVel (double n) { xVel = n; }
+    void setYVel (double n) { yVel = n; }
+
+    boolean isAlive()          { return alive; }
+    void setIsAlive(boolean n) { alive = n; }
+
+
+    /**
+     * Move 1 unit of time's worth of distance.  I.e. increment xPos by xVel
+     * and yPos by yVel.  If we move off an edge of the screen move us back
+     * in the opposite edge.
+     */
+    void clockTick() {
+        oldXPos = xPos;
+        oldYPos = yPos;
+        xPos = (xPos + xVel) % getGame().getWidth();
+        if(xPos < 0)
+            xPos += getGame().getWidth();
+        yPos = (yPos + yVel) % getGame().getHeight();
+        if(yPos < 0)
+            yPos += getGame().getHeight();
+    }
+
+    void accelerate(double dXVel, double dYVel) {
+        xVel += dXVel;
+        yVel += dYVel;
+    }
+
+    void die() {
+        getGame().getRegistry().unregister(this);
+    }
+
+    abstract int getSize();
+
+    /** resolve the effects of colliding with a space object.
+     *  @param obj the space object that this object is colliding with.
+     */
+    abstract void handleCollision(SpaceObject obj);
+}
diff --git a/tests/multiIncremental/SpaceWar/base/src/spacewar/Timer.java b/tests/multiIncremental/SpaceWar/base/src/spacewar/Timer.java
new file mode 100644 (file)
index 0000000..1f4a992
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+
+Copyright (c) Xerox Corporation 1998-2002.  All rights reserved.
+
+Use and copying of this software and preparation of derivative works based
+upon this software are permitted.  Any distribution of this software or
+derivative works must comply with all applicable United States export control
+laws.
+
+This software is made available AS IS, and Xerox Corporation makes no warranty
+about the software, its performance or its conformity to any specification.
+
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+|<---            this code is formatted to fit into 80 columns             --->|
+
+
+Timer.java
+Part of the Spacewar system.
+
+*/
+
+package spacewar;
+
+
+class Timer extends Thread {
+
+    private final static int TICK_PERIOD = 40;  // time between ticks in millis
+
+    private Game game;
+
+    Game getGame() { return game; }
+
+    Timer (Game theGame) {
+        super("Timer");
+        game = theGame;
+    }
+
+    public void run() {
+        long t1, tdiff;
+        while (true) {
+            t1 = System.currentTimeMillis();
+            getGame().clockTick();
+            tdiff = System.currentTimeMillis() - t1;
+            if (tdiff < TICK_PERIOD) {
+                try {
+                    sleep (Math.max(0 , TICK_PERIOD - tdiff));
+                }
+                catch (InterruptedException e) { }
+            }
+        }
+    }
+}