--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="lib" path="C:/AspectJ-Demo/apps/eclipse-3.0/plugins/org.aspectj.ajde_1.1.4/aspectjrt.jar"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>figures</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.ajdt.ui.ajbuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.ajdt.ui.ajnature</nature>
+ <nature>org.eclipse.ajdt.ui.ajnature</nature>
+ </natures>
+</projectDescription>
--- /dev/null
+<?xml version="1.0" ?>
+<!-- writen for Ant 1.5.1 -->
+<project name="figures" default="reset">
+
+<target name="reset" description="Reset the demonstration system sources">
+
+ <delete>
+ <fileset dir="src"/>
+ </delete>
+ <copy todir="src">
+ <fileset dir="src-fresh"/>
+ </copy>
+</target>
+
+</project>
\ No newline at end of file
--- /dev/null
+
+<head>
+<style>
+<!--
+ table.MsoNormalTable
+ {mso-style-parent:"";
+ font-size:10.0pt;
+ font-family:"Times New Roman";
+ }
+-->
+</style>
+<STYLE TYPE="text/css">
+<!--
+
+ /* FOR THE SDA PAGE */
+
+ /*
+ BODY {margin-top: 15px; margin-left: 15px; margin-right: 15px;}
+ */
+
+ A:link {
+ color:#4756AC;
+ }
+ A:visited {
+ color:#60657B;
+ }
+ A:hover {
+ color:red
+ }
+
+ INPUT {font:12px "Courier New", sans-serif;}
+
+ H2 {
+ font:18px/18px Verdana, Arial, Helvetica, sans-serif;
+ color:black;
+ font-weight:bold;
+ margin-left: 10px;
+ line-height:110%;
+ }
+ H3 {
+ font:18px/18px Verdana, Arial, Helvetica, sans-serif;
+ color:black;
+ font-weight:bold;
+ margin-left: 10px;
+ line-height:110%;
+ }
+ H4 {
+ font:15px/16px Verdana, Arial, Helvetica, sans-serif;
+ color:black;
+ font-weight:bold;
+ margin-left: 10px;
+ line-height:140%;
+ }
+ P {
+ font:13px/13px Verdana, Arial, Helvetica, sans-serif;
+ margin-right: 10px;
+ margin-left: 10px;
+ line-height:130%;
+ }
+ .paragraph {
+ font:13px/13px Verdana, Arial, Helvetica, sans-serif;
+ margin-right: 10px;
+ margin-left: 10px;
+ line-height:130%;
+ }
+ .smallParagraph {
+ font:11px/11px Verdana, Arial, Helvetica, sans-serif;
+ margin-right: 10px;
+ margin-left: 10px;
+ line-height:130%;
+ }
+ LI {
+ font:13px/13px Verdana, Arial, Helvetica, sans-serif;
+ text-align:justify;
+ margin-right: 10px;
+ margin-left: 15px;
+ line-height:120%;
+ }
+ /*
+ UL {
+ font:13px/13px Verdana, Arial, Helvetica, sans-serif;
+ text-align:justify;
+ margin-right: 10px;
+ margin-left: 15px;
+ line-height:120%;
+ }*/
+
+ DL {
+ font:13px/13px Verdana, Arial, Helvetica, sans-serif;
+ text-align:justify;
+ margin-right: 10px;
+ margin-left: 15px;
+ line-height:120%;
+ }
+ B { font:13px/13px Verdana, Arial, Helvetica, sans-serif;
+ font-weight:bold;
+ line-height:140%;
+ }
+ .footer {
+ font:10px/10px Verdana, Arial, Helvetica, sans-serif;
+ color:#888888;
+ text-align:left
+ }
+ .figureTitle {
+ font:13px/13px Verdana, Arial, Helvetica, sans-serif;
+ text-align:justify;
+ text-align:center
+ }
+ .copyrightNotice {
+ font:10px/10px Verdana, Arial, Helvetica, sans-serif;
+ color:#999999;
+ line-height:110%;
+ }
+ .smallHeading {
+ font:13px/13px Verdana, Arial, Helvetica, sans-serif;
+ font-weight:bold;
+ line-height:110%;
+ }
+ .tinyHeading {
+ font:11px/11px Verdana, Arial, Helvetica, sans-serif;
+ font-weight:bold;
+ line-height:120%;
+ }
+ .newsText {
+ font:11px/11px Verdana, Arial, Helvetica, sans-serif;
+ line-height:130%;
+ }
+ .smallParagraph {
+ font:11px/11px Verdana, Arial, Helvetica, sans-serif;
+ line-height:130%;
+ }
+ .fancyHeading {
+ font:20px/20px Chantilly, Arial, Helvetica, sans-serif;
+ margin-right: 10px;
+ color:#6f7a92;
+ margin-left: 10px;
+ line-height:130%;
+ }
+
+-->
+</STYLE>
+</head>
+
+<h2 align="center">AspectJ Figures Demo Instructions</h2>
+<h4>Setup</h4>
+<ul>
+ <li>
+ <p style="text-align: left">src: contains the end result of the demo, with the Canvas.updateHistory()
+ call refactored into the src/figures/support/HistoryUpdating.aj aspect</li>
+ <li>
+ <p style="text-align: left">src-fresh: contains the plain Java sources that the demo starts with</li>
+</ul>
+<h4>Reset </h4>
+<ul>
+ <li>run the "reset" target in build.xml to reset overwrite the sources in
+ src with those in src-fresh</li>
+</ul>
+<h4>Script</h4>
+<div align="center">
+ <table class="MsoNormalTable" border="0" cellspacing="3" cellpadding="0" style="width: 531.65pt; margin-left: -7.95pt" id="table1">
+ <tr style="height: 33.45pt">
+ <td style="width: 231px; height: 33.45pt; padding: 1.5pt; background: #F7F7F7">
+ <p class="MsoNormal" style="text-indent: -.25in; margin-left: 40.5pt">
+ <span style="font-size: 10.0pt; font-family: Symbol">·<span style="font:7.0pt "Times New Roman"">
+ </span></span><span style="font-size:10.0pt;font-family:
+ Tahoma">Show Figure editor running</span></p>
+ <p class="MsoNormal" style="text-indent: -.25in; margin-left: 40.5pt">
+ <span style="font-size: 10.0pt; font-family: Symbol">·<span style="font:7.0pt "Times New Roman"">
+ </span></span><span style="font-size:10.0pt;font-family:
+ Tahoma">Inspect Point.java</span></p>
+ <p class="MsoNormal" style="text-indent: -.25in; margin-left: 40.5pt">
+ <span style="font-size: 10.0pt; font-family: Symbol">·<span style="font:7.0pt "Times New Roman"">
+ </span></span><span style="font-size:10.0pt;font-family:
+ Tahoma">Use joinpoint probe or Eclipse search to find calls</span></td>
+ <td style="width: 461px; height: 33.45pt; padding: 1.5pt; background: #F7F7F7">
+ <p class="MsoNormal" style="margin-left:7.5pt">
+ <span style="font-family: Courier"><font size="1">call(void
+ figures.Canvas.updateHistory())</font></span></td>
+ </tr>
+ <tr style="height: 33.45pt">
+ <td style="width: 231px; height: 33.45pt; padding: 1.5pt; background: #F7F7F7">
+ <p class="MsoNormal" style="text-indent: -.25in; margin-left: 40.5pt">
+ <span style="font-size: 10.0pt; font-family: Symbol">·<span style="font:7.0pt "Times New Roman"">
+ </span></span><span style="font-size:10.0pt;font-family:
+ Tahoma">Describe places that it\92s called</span></p>
+ <p class="MsoNormal" style="text-indent: -.25in; margin-left: 40.5pt">
+ <span style="font-size: 10.0pt; font-family: Symbol">·<span style="font:7.0pt "Times New Roman"">
+ </span></span><span style="font-size:10.0pt;font-family:
+ Tahoma">create aspect (defines a special class that can crosscut other
+ classes)</span></p>
+ <p class="MsoNormal" style="text-indent: -.25in; margin-left: 40.5pt">
+ <span style="font-size: 10.0pt; font-family: Symbol">·<span style="font:7.0pt "Times New Roman"">
+ </span></span><span style="font-size:10.0pt;font-family:
+ Tahoma">aspect HistoryUpdating </span></p>
+ <p class="MsoNormal" style="text-indent: -.25in; margin-left: 40.5pt">
+ <span style="font-size: 10.0pt; font-family: Symbol">·<span style="font:7.0pt "Times New Roman"">
+ </span></span><span style="font-size:10.0pt;font-family:
+ Tahoma">write pointcut (has name and parameters)</span></td>
+ <td style="width: 461px; height: 33.45pt; padding: 1.5pt; background: #F7F7F7">
+ <p class="MsoNormal" style="margin-left:7.5pt">
+ <span style="font-family: Courier"><font size="1">pointcut moves():
+ </font> </span></p>
+ <p class="MsoNormal" style="margin-left:7.5pt">
+ <span style="font-family: Courier"><font size="1"> execution(void
+ Line.setP1(Point)) || </font> </span></p>
+ <p class="MsoNormal" style="margin-left:7.5pt">
+ <span style="font-family: Courier"><font size="1"> execution(void
+ Line.setP2(Point));</font></span></td>
+ </tr>
+ <tr style="height: 33.45pt">
+ <td style="width: 231px; height: 33.45pt; padding: 1.5pt; background: #F7F7F7">
+ <p class="MsoNormal" style="text-indent: -.25in; margin-left: 40.5pt">
+ <span style="font-size: 10.0pt; font-family: Symbol">·<span style="font:7.0pt "Times New Roman"">
+ </span></span><span style="font-size:10.0pt;font-family:
+ Tahoma">write after advice (runs \93on the way back out\94)</span></td>
+ <td style="width: 461px; height: 33.45pt; padding: 1.5pt; background: #F7F7F7">
+ <p class="MsoNormal" style="margin-left:7.5pt">
+ <span style="font-family: Courier"><font size="1">after() returning: move() { <br>
+ <runs after each move> }</font></span></td>
+ </tr>
+ <tr style="height: 33.45pt">
+ <td style="width: 231px; height: 33.45pt; padding: 1.5pt; background: #F7F7F7">
+ <p class="MsoNormal" style="text-indent: -.25in; margin-left: 40.5pt">
+ <span style="font-size: 10.0pt; font-family: Symbol">·<span style="font:7.0pt "Times New Roman"">
+ </span></span><span style="font-size:10.0pt;font-family:
+ Tahoma">extend advice to Point setters (multi-class)</span></td>
+ <td style="width: 461px; height: 33.45pt; padding: 1.5pt; background: #F7F7F7">
+ <p class="MsoNormal" style="margin-left:7.5pt">
+ <span style="font-family: Courier"><font size="1">call(void FigureElement+.set*(..))</font></span></td>
+ </tr>
+ <tr style="height: 33.45pt">
+ <td style="width: 231px; height: 33.45pt; padding: 1.5pt; background: #F7F7F7">
+ <p class="MsoNormal" style="text-indent: -.25in; margin-left: 40.5pt">
+ <span style="font-size: 10.0pt; font-family: Symbol">·<span style="font:7.0pt "Times New Roman"">
+ </span></span><span style="font-size:10.0pt;font-family:
+ Tahoma">capture context & use interface</span></td>
+ <td style="width: 461px; height: 33.45pt; padding: 1.5pt; background: #F7F7F7">
+ <p class="MsoNormal" style="margin-left:7.5pt">
+ <span style="font-family: Courier"><font size="1">move(FigureElement fe):
+ this(fe)
+ &&..</font></span></td>
+ </tr>
+ <tr style="height: 33.45pt">
+ <td style="width: 231px; height: 33.45pt; padding: 1.5pt; background: #F7F7F7">
+ <p class="MsoNormal" style="text-indent: -.25in; margin-left: 40.5pt">
+ <span style="font-size: 10.0pt; font-family: Symbol">·<span style="font:7.0pt "Times New Roman"">
+ </span></span><span style="font-size:10.0pt;font-family:
+ Tahoma">Show structure, note that SlothfulPoint is now included</span></p>
+ <p class="MsoNormal" style="text-indent: -.25in; margin-left: 40.5pt">
+ <span style="font-size: 10.0pt; font-family: Symbol">·<span style="font:7.0pt "Times New Roman"">
+ </span></span><span style="font-size:10.0pt;font-family:
+ Tahoma">Run & show effect</span></td>
+ <td style="width: 461px; height: 33.45pt; padding: 1.5pt; background: #F7F7F7">
+ <p class="MsoNormal" style="margin-left:7.5pt">
+ <span style="font-family: Courier"><font size="1"> </font></span></td>
+ </tr>
+ <tr style="height: 33.45pt">
+ <td style="width: 231px; height: 33.45pt; padding: 1.5pt; background: #F7F7F7">
+ <p class="MsoNormal" style="text-indent: -.25in; margin-left: 40.5pt">
+ <span style="font-size: 10.0pt; font-family: Symbol">·<span style="font:7.0pt "Times New Roman"">
+ </span></span><span style="font-size:10.0pt;font-family:
+ Tahoma">Show Point.moveBy history violoation</span></p>
+ <p class="MsoNormal" style="text-indent: -.25in; margin-left: 40.5pt">
+ <span style="font-size: 10.0pt; font-family: Symbol">·<span style="font:7.0pt "Times New Roman"">
+ </span></span><span style="font-size:10.0pt;font-family:
+ Tahoma">Want to make sure that sets of private fields of classes implementing
+ FigureElement only happen from within the set methods</span></td>
+ <td style="width: 461px; height: 33.45pt; padding: 1.5pt; background: #F7F7F7">
+ <p class="MsoNormal" style="margin-left:7.5pt">
+ <span style="font-family: Courier"><font size="1">declare warning:
+ </font> </span></p>
+ <p class="MsoNormal" style="margin-left:7.5pt">
+ <span style="font-family: Courier"><font size="1"> set(private * FigureElement+.*) <br>
+ && !(withincode(*
+ FigureElement+.set*(..)) || <br>
+ withincode(FigureElement+.new(..))):</font></span></p>
+ <p class="MsoNormal" style="margin-left:7.5pt">
+ <span style="font-family: Courier"><font size="1"> "should only assign to fields
+ from set methods";</font></span></td>
+ </tr>
+ <tr style="height: 33.45pt">
+ <td style="width: 231px; height: 33.45pt; padding: 1.5pt; background: #F7F7F7">
+ <p class="MsoNormal" style="text-indent: -.25in; margin-left: 40.5pt">
+ <span style="font-size: 10.0pt; font-family: Symbol">·<span style="font:7.0pt "Times New Roman"">
+ </span></span><span style="font-size:10.0pt;font-family:
+ Tahoma">Write before advice that does precondition checking on Points.</span></td>
+ <td style="width: 461px; height: 33.45pt; padding: 1.5pt; background: #F7F7F7">
+ <p class="MsoNormal" style="margin-left:7.5pt">
+ <span style="font-family:"Courier New""><font size="2">before(int</font><font size="2">
+ newValue): <br>
+ set(int Point.*) && args(newValue) {<br>
+ if (newValue < 0) {<br>
+ throw new IAE("too small");<br>
+ } <br>
+ }</font></span></td>
+ </tr>
+ </table>
+</div>
+<p> </p>
--- /dev/null
+package figures.support;
+
+import figures.*;
+
+public aspect HistoryUpdating {
+
+ pointcut moves(FigureElement fe):
+ this(fe) &&
+ execution(void FigureElement+.set*(..));
+
+ after(FigureElement fe) returning: moves(fe) {
+ Canvas.updateHistory(fe);
+ }
+
+ declare error:
+ set(private * FigureElement+.*) &&
+ !(withincode(void FigureElement+.set*(..)) ||
+ withincode(FigureElement+.new(..))):
+ "doh!!!";
+
+ before(int newValue):
+ set(int Point.*) && args(newValue) {
+ if (newValue < 0) {
+ throw new IllegalArgumentException("too small");
+ }
+ }
+}
--- /dev/null
+/*
+Copyright (c) 2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures;
+
+import java.awt.*;
+import java.awt.geom.*;
+
+public class Box extends ShapeFigureElement {
+ private Point _p0;
+ private Point _p1;
+ private Point _p2;
+ private Point _p3;
+
+ public Box(int x0, int y0, int width, int height) {
+ _p0 = new Point(x0, y0);
+ _p1 = new Point(x0+width, y0);
+ _p2 = new Point(x0+width, y0+height);
+ _p3 = new Point(x0, y0+height);
+ }
+
+ public Point getP0() { return _p0; }
+ public Point getP1() { return _p1; }
+ public Point getP2() { return _p2; }
+ public Point getP3() { return _p3; }
+
+ public void move(int dx, int dy) {
+ _p0.move(dx, dy);
+ _p1.move(dx, dy);
+ _p2.move(dx, dy);
+ _p3.move(dx, dy);
+ }
+
+ public void checkBoxness() {
+ if ((_p0.getX() == _p3.getX()) &&
+ (_p1.getX() == _p2.getX()) &&
+ (_p0.getY() == _p1.getY()) &&
+ (_p2.getY() == _p3.getY()))
+ return;
+ throw new IllegalStateException("This is not a square.");
+ }
+
+ public String toString() {
+ return "Box(" + _p0 + ", " + _p1 + ", " + _p2 + ", " + _p3 + ")";
+ }
+
+ public Shape getShape() {
+ return new Rectangle(getP1().getX(),
+ getP1().getY(),
+ getP3().getX() - getP1().getX(),
+ getP3().getY() - getP1().getY());
+ }
+}
+
--- /dev/null
+/*
+Copyright (c) 2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures;
+
+import figures.support.Log;
+
+public class Canvas {
+ public static void updateHistory() { }
+
+ public static void updateHistory(FigureElement fe) {
+ System.out.println("> updating history for: " + fe);
+ }
+}
--- /dev/null
+/*
+Copyright (c) 2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures;
+
+import java.awt.Color;
+import figures.FigureElement;
+
+public aspect ColorControl {
+ public static void setFillColor(FigureElement fe, Color color) {
+ // fill in here
+ }
+
+ public static void setLineColor(FigureElement fe, Color color) {
+ // fill in here
+ }
+
+ // fill in here
+}
--- /dev/null
+/*
+Copyright (c) 2001-2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures;
+
+import java.awt.*;
+import java.awt.geom.*;
+
+public interface FigureElement {
+ public static final int MIN_VALUE = 0;
+ public static final int MAX_VALUE = 500;
+
+ public abstract void move(int dx, int dy);
+
+ public abstract Rectangle getBounds();
+
+ public abstract boolean contains(Point2D p);
+
+ public abstract void paint(Graphics2D g2);
+}
--- /dev/null
+/*
+Copyright (c) 2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures;
+
+import java.util.*;
+import java.awt.*;
+import java.awt.geom.*;
+
+public class Group implements FigureElement {
+ private Collection _members;
+ private String _identifier;
+
+ public Group(FigureElement first) {
+ this._members = new ArrayList();
+ add(first);
+ }
+
+ public void add(FigureElement fe) {
+ _members.add(fe);
+ }
+
+ public Iterator members() {
+ return _members.iterator();
+ }
+
+ public void move(int dx, int dy) {
+ for (Iterator i = _members.iterator(); i.hasNext(); ) {
+ FigureElement fe = (FigureElement)i.next();
+ fe.move(dx, dy);
+ }
+ }
+
+ public void resetIdentifier(String identifier) {
+ resetIdentifier(identifier);
+ }
+
+ public String toString() {
+ if (_identifier != null) {
+ return _identifier;
+ }
+
+ StringBuffer buf = new StringBuffer("Group(");
+ for (Iterator i = _members.iterator(); i.hasNext(); ) {
+ buf.append(i.next().toString());
+ if (i.hasNext()) {
+ buf.append(", ");
+ }
+ }
+ buf.append(")");
+ return buf.toString();
+ }
+
+ public Rectangle getBounds() {
+ Rectangle previous = null;
+ for (Iterator i = _members.iterator(); i.hasNext(); ) {
+ FigureElement fe = (FigureElement)i.next();
+ Rectangle rect = fe.getBounds();
+ if (previous != null) {
+ previous = previous.union(rect);
+ } else {
+ previous = rect;
+ }
+ }
+ return previous;
+ }
+
+ public boolean contains(Point2D p) {
+ for (Iterator i = _members.iterator(); i.hasNext(); ) {
+ FigureElement fe = (FigureElement)i.next();
+ if (fe.contains(p)) return true;
+ }
+ return false;
+ }
+
+ public void paint(Graphics2D g2) {
+ for (Iterator i = _members.iterator(); i.hasNext(); ) {
+ FigureElement fe = (FigureElement)i.next();
+ fe.paint(g2);
+ }
+ }
+
+ public int size() {
+ return _members.size();
+ }
+}
+
--- /dev/null
+/*
+Copyright (c) 2001-2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures;
+
+import java.awt.*;
+import java.awt.geom.*;
+
+public class Line extends ShapeFigureElement {
+ private Point _p1;
+ private Point _p2;
+
+ public Line(Point p1, Point p2) {
+ _p1 = p1;
+ _p2 = p2;
+ }
+
+ public Point getP1() {
+ return _p1;
+ }
+
+ public void setP1(Point p1) {
+ _p1 = p1;
+ Canvas.updateHistory();
+ }
+
+ public Point getP2() {
+ return _p2;
+ }
+
+ public void setP2(Point p2) {
+ _p2 = p2;
+ Canvas.updateHistory();
+ }
+
+ public void move(int dx, int dy) {
+ _p1.move(dx, dy);
+ _p2.move(dx, dy);
+ }
+
+ public String toString() {
+ return "Line(" + _p1 + ", " + _p2 + ")";
+ }
+
+ /**
+ * Used to determine if this line {@link contains(Point2D)} a point.
+ */
+ final static int THRESHHOLD = 5;
+
+ /**
+ * Returns <code>true</code> if the point segment distance is less than
+ * {@link THRESHHOLD}.
+ */
+ public boolean contains(Point2D p) {
+ return getLine2D().ptLineDist(p) < THRESHHOLD;
+ }
+
+ private Line2D getLine2D() {
+ return new Line2D.Float((float)getP1().getX(),
+ (float)getP1().getY(),
+ (float)getP2().getX(),
+ (float)getP2().getY());
+ }
+
+ public Shape getShape() {
+ return getLine2D();
+ }
+}
+
--- /dev/null
+/*
+Copyright (c) 2001-2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures;
+
+import java.awt.*;
+import java.awt.geom.*;
+
+public class Point extends ShapeFigureElement {
+ private int _x;
+ private int _y;
+
+ public Point(int x, int y) {
+ _x = x;
+ _y = y;
+ }
+
+ public int getX() {
+ return _x;
+ }
+
+ public void setX(int x) {
+ _x = x;
+ Canvas.updateHistory();
+ }
+
+ public int getY() {
+ return _y;
+ }
+
+ public void setY(int y) {
+ _y = y;
+ Canvas.updateHistory();
+ }
+
+ public void move(int dx, int dy) {
+ _x += dx;
+ _y += dy;
+ }
+
+ public String toString() {
+ return "Point(" + _x + ", " + _y + ")";
+ }
+
+ /** The height of displayed {@link Point}s. */
+ private final static int HEIGHT = 10;
+
+ /** The width of displayed {@link Point}s. -- same as {@link HEIGHT}. */
+ private final static int WIDTH = Point.HEIGHT;
+
+ public Shape getShape() {
+ return new Ellipse2D.Float((float)getX()-Point.WIDTH/2,
+ (float)getY()-Point.HEIGHT/2,
+ (float)Point.HEIGHT,
+ (float)Point.WIDTH);
+ }
+}
+
--- /dev/null
+/*
+Copyright (c) 2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures;
+
+import java.awt.*;
+import java.awt.geom.*;
+
+public abstract class ShapeFigureElement implements FigureElement {
+ public abstract void move(int dx, int dy);
+
+ public abstract Shape getShape();
+
+ public Rectangle getBounds() {
+ return getShape().getBounds();
+ }
+
+ public boolean contains(Point2D p) {
+ return getShape().contains(p);
+ }
+
+ public Color getLineColor() {
+ return Color.black;
+ }
+
+ public Color getFillColor() {
+ return Color.red;
+ }
+
+ public final void paint(Graphics2D g2) {
+ Shape shape = getShape();
+ g2.setPaint(getFillColor());
+ g2.fill(shape);
+ g2.setPaint(getLineColor());
+ g2.draw(shape);
+ }
+}
--- /dev/null
+/*
+Copyright (c) 2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures;
+
+import java.awt.*;
+import java.awt.geom.*;
+
+/**
+ * This class makes mistakes to be caught by invariant checkers.
+ */
+public class SlothfulPoint extends ShapeFigureElement {
+ private int _x;
+ private int _y;
+
+ public SlothfulPoint(int x, int y) {
+ }
+
+ public void setX(int x) {
+ _x = x;
+ }
+
+ public void setY(int y) {
+ _y = y;
+ }
+
+ public void move(int dx, int dy) {
+ setX(_x + dx);
+ setY(_y + dy);
+ }
+
+ public String toString() {
+ return "SlothfulPoint";
+ }
+
+ public Shape getShape() {
+ return new Ellipse2D.Float((float)_x,
+ (float)_y, 1.0f, 1.0f);
+ }
+}
+
--- /dev/null
+/*
+Copyright (c) 2001-2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures.gui;
+
+import figures.Point;
+import figures.Line;
+import figures.FigureElement;
+import figures.Group;
+
+
+import java.awt.*;
+import java.awt.geom.*;
+import java.awt.event.*;
+import java.io.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.text.*;
+import javax.swing.border.*;
+
+public class FigurePanel extends JComponent {
+
+ ButtonsPanel bp = new ButtonsPanel();
+ FigureSurface fs = new FigureSurface();
+ ConsolePanel cp = new ConsolePanel();
+
+
+ public FigurePanel() {
+ setLayout(new BorderLayout());
+ JPanel panel = new JPanel();
+ panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+ panel.add(fs);
+ panel.add(bp);
+ JSplitPane sp = new JSplitPane(JSplitPane.VERTICAL_SPLIT, panel, cp);
+ sp.setPreferredSize(new Dimension(500, 400));
+ sp.setDividerLocation(250);
+ add(BorderLayout.CENTER, sp);
+ }
+
+ class ButtonsPanel extends JPanel {
+ private JLabel msgs = new JLabel("click to add a point or line");
+ public ButtonsPanel() {
+ setLayout(new FlowLayout(FlowLayout.LEFT));
+// add(new JButton(new AbstractAction("Main") {
+// public void actionPerformed(ActionEvent e) {
+// Main.main(new String[]{});
+// fs.repaint();
+// }
+// }));
+ add(msgs);
+ }
+
+ public void log(String msg) {
+ msgs.setText(msg);
+ }
+ }
+
+ static class ConsolePanel extends JPanel {
+
+ JTextArea text = new JTextArea();
+
+ public ConsolePanel() {
+ super(new BorderLayout());
+ text.setFont(StyleContext.getDefaultStyleContext().getFont("SansSerif", Font.PLAIN, 10));
+ JScrollPane scroller = new JScrollPane(text);
+ scroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
+ add(BorderLayout.CENTER, scroller);
+ }
+
+ public void println(String msg) {
+ text.append(msg + '\n');
+ }
+ }
+
+ final static Color BACKGROUND = Color.white;
+
+ static class FigureSurface extends JPanel implements MouseListener, MouseMotionListener {
+ private Group canvas;
+
+ public FigureSurface() {
+ canvas = new Group(new Point(250, 250));
+ addMouseMotionListener(this);
+ addMouseListener(this);
+ setPreferredSize(new Dimension(500,500));
+ }
+
+ private Point addPoint(int x, int y) {
+ Point p = new Point(x, y);
+ canvas.add(p);
+ repaint();
+ return p;
+ }
+
+ private Line addLine(Point p1, Point p2) {
+ if (Math.abs(p1.getX()-p2.getX()) < 5 ||
+ Math.abs(p1.getY()-p2.getY()) < 5) {
+ return null;
+ }
+
+ Line line = null;
+ if (p1 != null && p2 != null) {
+ line = new Line(p1, p2);
+ canvas.add(line);
+ }
+ repaint();
+ return line;
+ }
+
+ public void paint(Graphics g) {
+ Graphics2D g2 = (Graphics2D) g;
+ g2.setPaint(BACKGROUND);
+ g2.fill(new Rectangle2D.Float(0f, 0f, (float)g2.getClipBounds().width, (float)g2.getClipBounds().height));
+ g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ canvas.paint(g2);
+ }
+
+
+ int lastX, lastY;
+ int pressX, pressY;
+
+ FigureElement first = null;
+ Point point1 = null;
+
+ public void mousePressed(MouseEvent e){
+ int x = e.getX(), y = e.getY();
+ pressX = lastX = x; pressY = lastY = y;
+ first = findFigureElement(x, y);
+ if (first == null) {
+ point1 = addPoint(x, y);
+ }
+ }
+
+ public void mouseDragged(MouseEvent e) {
+ int x = e.getX(), y = e.getY(), dx = lastX-x, dy = lastY-y;
+ lastX = x;
+ lastY = y;
+ if (first == null) {
+ Line line = addLine(point1, new Point(x, y));
+ if (line != null) {
+ canvas.add(line.getP2());
+ first = line.getP2();
+ canvas.add(line);
+ }
+ } else {
+ first.move(-dx, -dy);
+ }
+ repaint();
+ }
+
+ public void mouseReleased(MouseEvent e){
+ mouseDragged(e);
+ first = null;
+ point1 = null;
+ }
+
+
+ public void mouseMoved(MouseEvent e){}
+ public void mouseClicked(MouseEvent e){}
+ public void mouseExited(MouseEvent e){}
+ public void mouseEntered(MouseEvent e){}
+
+ private FigureElement findFigureElement(int x, int y) {
+ Point2D p = new Point2D.Float((float)x, (float)y);
+ for (Iterator i = canvas.members(); i.hasNext(); ) {
+ FigureElement fe = (FigureElement)i.next();
+ if (fe.contains(p)) return fe;
+ }
+ return null;
+ }
+ }
+}
--- /dev/null
+/*
+Copyright (c) 2001-2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures.gui;
+
+import figures.support.Log;
+
+aspect LogAdapter {
+
+ before(String s): call(void Log.log(String)) && args(s) {
+ if (Main.panel != null) {
+ Main.panel.cp.println(s);
+ }
+ }
+}
--- /dev/null
+/*
+Copyright (c) 2001-2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures.gui;
+
+import javax.swing.*;
+import figures.support.Log;
+import figures.Point;
+
+public class Main {
+ static FigurePanel panel;
+
+ public static void main(String[] args) {
+ JFrame figureFrame = new JFrame("Figure Editor");
+ panel = new FigurePanel();
+ figureFrame.setContentPane(panel);
+ figureFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ figureFrame.pack();
+ figureFrame.setVisible(true);
+
+// Point p = new Point(0, 0);
+// p.setX(-10);
+ }
+}
--- /dev/null
+/*
+Copyright (c) 2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures.support;
+
+public class Log {
+ private static StringBuffer data = new StringBuffer();
+
+ public static void traceObject(Object o) {
+ throw new UnsupportedOperationException();
+ }
+
+ public static void log(String s) {
+ data.append(s);
+ data.append(';');
+ }
+
+ public static void logClassName(Class _class) {
+ String name = _class.getName();
+ int dot = name.lastIndexOf('.');
+ if (dot == -1) {
+ log(name);
+ } else {
+ log(name.substring(dot+1, name.length()));
+ }
+ }
+
+ public static String getString() {
+ return data.toString();
+ }
+
+ public static void clear() {
+ data.setLength(0);
+ }
+}
--- /dev/null
+/*
+Copyright (c) 2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures;
+
+import java.awt.*;
+import java.awt.geom.*;
+
+public class Box extends ShapeFigureElement {
+ private Point _p0;
+ private Point _p1;
+ private Point _p2;
+ private Point _p3;
+
+ public Box(int x0, int y0, int width, int height) {
+ _p0 = new Point(x0, y0);
+ _p1 = new Point(x0+width, y0);
+ _p2 = new Point(x0+width, y0+height);
+ _p3 = new Point(x0, y0+height);
+ }
+
+ public Point getP0() { return _p0; }
+ public Point getP1() { return _p1; }
+ public Point getP2() { return _p2; }
+ public Point getP3() { return _p3; }
+
+ public void move(int dx, int dy) {
+ _p0.move(dx, dy);
+ _p1.move(dx, dy);
+ _p2.move(dx, dy);
+ _p3.move(dx, dy);
+ }
+
+ public void checkBoxness() {
+ if ((_p0.getX() == _p3.getX()) &&
+ (_p1.getX() == _p2.getX()) &&
+ (_p0.getY() == _p1.getY()) &&
+ (_p2.getY() == _p3.getY()))
+ return;
+ throw new IllegalStateException("This is not a square.");
+ }
+
+ public String toString() {
+ return "Box(" + _p0 + ", " + _p1 + ", " + _p2 + ", " + _p3 + ")";
+ }
+
+ public Shape getShape() {
+ return new Rectangle(getP1().getX(),
+ getP1().getY(),
+ getP3().getX() - getP1().getX(),
+ getP3().getY() - getP1().getY());
+ }
+}
+
--- /dev/null
+/*
+Copyright (c) 2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures;
+
+import figures.support.Log;
+
+public class Canvas {
+ public static void updateHistory() { }
+
+ public static void updateHistory(FigureElement fe) {
+ System.out.println("> updating history for: " + fe);
+ }
+}
--- /dev/null
+package figures;
+
+import java.awt.Color;
+import figures.FigureElement;
+
+public aspect ColorControl {
+ public static void setFillColor(FigureElement fe, Color color) {
+ // fill in here
+ }
+
+ public static void setLineColor(FigureElement fe, Color color) {
+ // fill in here
+ }
+
+ // fill in here
+}
--- /dev/null
+/*
+Copyright (c) 2001-2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures;
+
+import java.awt.*;
+import java.awt.geom.*;
+
+public interface FigureElement {
+ public static final int MIN_VALUE = 0;
+ public static final int MAX_VALUE = 500;
+
+ public abstract void move(int dx, int dy);
+
+ public abstract Rectangle getBounds();
+
+ public abstract boolean contains(Point2D p);
+
+ public abstract void paint(Graphics2D g2);
+}
--- /dev/null
+/*
+Copyright (c) 2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures;
+
+import java.util.*;
+import java.awt.*;
+import java.awt.geom.*;
+
+public class Group implements FigureElement {
+ private Collection _members;
+ private String _identifier;
+
+ public Group(FigureElement first) {
+ this._members = new ArrayList();
+ add(first);
+ }
+
+ public void add(FigureElement fe) {
+ _members.add(fe);
+ }
+
+ public Iterator members() {
+ return _members.iterator();
+ }
+
+ public void move(int dx, int dy) {
+ for (Iterator i = _members.iterator(); i.hasNext(); ) {
+ FigureElement fe = (FigureElement)i.next();
+ fe.move(dx, dy);
+ }
+ }
+
+ public void resetIdentifier(String identifier) {
+ resetIdentifier(identifier);
+ }
+
+ public String toString() {
+ if (_identifier != null) {
+ return _identifier;
+ }
+
+ StringBuffer buf = new StringBuffer("Group(");
+ for (Iterator i = _members.iterator(); i.hasNext(); ) {
+ buf.append(i.next().toString());
+ if (i.hasNext()) {
+ buf.append(", ");
+ }
+ }
+ buf.append(")");
+ return buf.toString();
+ }
+
+ public Rectangle getBounds() {
+ Rectangle previous = null;
+ for (Iterator i = _members.iterator(); i.hasNext(); ) {
+ FigureElement fe = (FigureElement)i.next();
+ Rectangle rect = fe.getBounds();
+ if (previous != null) {
+ previous = previous.union(rect);
+ } else {
+ previous = rect;
+ }
+ }
+ return previous;
+ }
+
+ public boolean contains(Point2D p) {
+ for (Iterator i = _members.iterator(); i.hasNext(); ) {
+ FigureElement fe = (FigureElement)i.next();
+ if (fe.contains(p)) return true;
+ }
+ return false;
+ }
+
+ public void paint(Graphics2D g2) {
+ for (Iterator i = _members.iterator(); i.hasNext(); ) {
+ FigureElement fe = (FigureElement)i.next();
+ fe.paint(g2);
+ }
+ }
+
+ public int size() {
+ return _members.size();
+ }
+}
+
--- /dev/null
+/*
+Copyright (c) 2001-2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures;
+
+import java.awt.*;
+import java.awt.geom.*;
+
+public class Line extends ShapeFigureElement {
+ private Point _p1;
+ private Point _p2;
+
+ public Line(Point p1, Point p2) {
+ _p1 = p1;
+ _p2 = p2;
+ }
+
+ public Point getP1() {
+ return _p1;
+ }
+
+ public void setP1(Point p1) {
+ _p1 = p1;
+// Canvas.updateHistory();
+ }
+
+ public Point getP2() {
+ return _p2;
+ }
+
+ public void setP2(Point p2) {
+ _p2 = p2;
+// Canvas.updateHistory();
+ }
+
+ public void move(int dx, int dy) {
+ _p1.move(dx, dy);
+ _p2.move(dx, dy);
+ }
+
+ public String toString() {
+ return "Line(" + _p1 + ", " + _p2 + ")";
+ }
+
+ /**
+ * Used to determine if this line {@link contains(Point2D)} a point.
+ */
+ final static int THRESHHOLD = 5;
+
+ /**
+ * Returns <code>true</code> if the point segment distance is less than
+ * {@link THRESHHOLD}.
+ */
+ public boolean contains(Point2D p) {
+ return getLine2D().ptLineDist(p) < THRESHHOLD;
+ }
+
+ private Line2D getLine2D() {
+ return new Line2D.Float((float)getP1().getX(),
+ (float)getP1().getY(),
+ (float)getP2().getX(),
+ (float)getP2().getY());
+ }
+
+ public Shape getShape() {
+ return getLine2D();
+ }
+}
+
--- /dev/null
+/*
+Copyright (c) 2001-2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures;
+
+import java.awt.*;
+import java.awt.geom.*;
+
+public class Point extends ShapeFigureElement {
+ private int _x;
+ private int _y;
+
+ public Point(int x, int y) {
+ _x = x;
+ _y = y;
+ }
+
+ public int getX() {
+ return _x;
+ }
+
+ public void setX(int x) {
+ _x = x;
+// Canvas.updateHistory();
+ }
+
+ public int getY() {
+ return _y;
+ }
+
+ public void setY(int y) {
+ _y = y;
+// Canvas.updateHistory();
+ }
+
+ public void move(int dx, int dy) {
+ setX(_x + dx);
+ setY(_y + dy);
+ }
+
+ public String toString() {
+ return "Point(" + _x + ", " + _y + ")";
+ }
+
+ /** The height of displayed {@link Point}s. */
+ private final static int HEIGHT = 10;
+
+ /** The width of displayed {@link Point}s. -- same as {@link HEIGHT}. */
+ private final static int WIDTH = Point.HEIGHT;
+
+ public Shape getShape() {
+ return new Ellipse2D.Float((float)getX()-Point.WIDTH/2,
+ (float)getY()-Point.HEIGHT/2,
+ (float)Point.HEIGHT,
+ (float)Point.WIDTH);
+ }
+}
+
--- /dev/null
+/*
+Copyright (c) 2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures;
+
+import java.awt.*;
+import java.awt.geom.*;
+
+public abstract class ShapeFigureElement implements FigureElement {
+ public abstract void move(int dx, int dy);
+
+ public abstract Shape getShape();
+
+ public Rectangle getBounds() {
+ return getShape().getBounds();
+ }
+
+ public boolean contains(Point2D p) {
+ return getShape().contains(p);
+ }
+
+ public Color getLineColor() {
+ return Color.black;
+ }
+
+ public Color getFillColor() {
+ return Color.red;
+ }
+
+ public final void paint(Graphics2D g2) {
+ Shape shape = getShape();
+ g2.setPaint(getFillColor());
+ g2.fill(shape);
+ g2.setPaint(getLineColor());
+ g2.draw(shape);
+ }
+}
--- /dev/null
+/*
+Copyright (c) 2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures;
+
+import java.awt.*;
+import java.awt.geom.*;
+
+/**
+ * This class makes mistakes to be caught by invariant checkers.
+ */
+public class SlothfulPoint extends ShapeFigureElement {
+ private int _x;
+ private int _y;
+
+ public SlothfulPoint(int x, int y) {
+ }
+
+ public void setX(int x) {
+ _x = x;
+ }
+
+ public void setY(int y) {
+ _y = y;
+ }
+
+ public void move(int dx, int dy) {
+ setX(_x + dx);
+ setY(_y + dy);
+ }
+
+ public String toString() {
+ return "SlothfulPoint";
+ }
+
+ public Shape getShape() {
+ return new Ellipse2D.Float((float)_x,
+ (float)_y, 1.0f, 1.0f);
+ }
+}
+
--- /dev/null
+/*
+Copyright (c) 2001-2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures.gui;
+
+import figures.Point;
+import figures.Line;
+import figures.FigureElement;
+import figures.Group;
+
+
+import java.awt.*;
+import java.awt.geom.*;
+import java.awt.event.*;
+import java.io.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.text.*;
+import javax.swing.border.*;
+
+public class FigurePanel extends JComponent {
+
+ ButtonsPanel bp = new ButtonsPanel();
+ FigureSurface fs = new FigureSurface();
+ ConsolePanel cp = new ConsolePanel();
+
+
+ public FigurePanel() {
+ setLayout(new BorderLayout());
+ JPanel panel = new JPanel();
+ panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+ panel.add(fs);
+ panel.add(bp);
+ JSplitPane sp = new JSplitPane(JSplitPane.VERTICAL_SPLIT, panel, cp);
+ sp.setPreferredSize(new Dimension(500, 400));
+ sp.setDividerLocation(250);
+ add(BorderLayout.CENTER, sp);
+ }
+
+ class ButtonsPanel extends JPanel {
+ private JLabel msgs = new JLabel("click to add a point or line");
+ public ButtonsPanel() {
+ setLayout(new FlowLayout(FlowLayout.LEFT));
+// add(new JButton(new AbstractAction("Main") {
+// public void actionPerformed(ActionEvent e) {
+// Main.main(new String[]{});
+// fs.repaint();
+// }
+// }));
+ add(msgs);
+ }
+
+ public void log(String msg) {
+ msgs.setText(msg);
+ }
+ }
+
+ static class ConsolePanel extends JPanel {
+
+ JTextArea text = new JTextArea();
+
+ public ConsolePanel() {
+ super(new BorderLayout());
+ text.setFont(StyleContext.getDefaultStyleContext().getFont("SansSerif", Font.PLAIN, 10));
+ JScrollPane scroller = new JScrollPane(text);
+ scroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
+ add(BorderLayout.CENTER, scroller);
+ }
+
+ public void println(String msg) {
+ text.append(msg + '\n');
+ }
+ }
+
+ final static Color BACKGROUND = Color.white;
+
+ static class FigureSurface extends JPanel implements MouseListener, MouseMotionListener {
+ private Group canvas;
+
+ public FigureSurface() {
+ canvas = new Group(new Point(250, 250));
+ addMouseMotionListener(this);
+ addMouseListener(this);
+ setPreferredSize(new Dimension(500,500));
+ }
+
+ private Point addPoint(int x, int y) {
+ Point p = new Point(x, y);
+ canvas.add(p);
+ repaint();
+ return p;
+ }
+
+ private Line addLine(Point p1, Point p2) {
+ if (Math.abs(p1.getX()-p2.getX()) < 5 ||
+ Math.abs(p1.getY()-p2.getY()) < 5) {
+ return null;
+ }
+
+ Line line = null;
+ if (p1 != null && p2 != null) {
+ line = new Line(p1, p2);
+ canvas.add(line);
+ }
+ repaint();
+ return line;
+ }
+
+ public void paint(Graphics g) {
+ Graphics2D g2 = (Graphics2D) g;
+ g2.setPaint(BACKGROUND);
+ g2.fill(new Rectangle2D.Float(0f, 0f, (float)g2.getClipBounds().width, (float)g2.getClipBounds().height));
+ g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ canvas.paint(g2);
+ }
+
+
+ int lastX, lastY;
+ int pressX, pressY;
+
+ FigureElement first = null;
+ Point point1 = null;
+
+ public void mousePressed(MouseEvent e){
+ int x = e.getX(), y = e.getY();
+ pressX = lastX = x; pressY = lastY = y;
+ first = findFigureElement(x, y);
+ if (first == null) {
+ point1 = addPoint(x, y);
+ }
+ }
+
+ public void mouseDragged(MouseEvent e) {
+ int x = e.getX(), y = e.getY(), dx = lastX-x, dy = lastY-y;
+ lastX = x;
+ lastY = y;
+ if (first == null) {
+ Line line = addLine(point1, new Point(x, y));
+ if (line != null) {
+ canvas.add(line.getP2());
+ first = line.getP2();
+ canvas.add(line);
+ }
+ } else {
+ first.move(-dx, -dy);
+ }
+ repaint();
+ }
+
+ public void mouseReleased(MouseEvent e){
+ mouseDragged(e);
+ first = null;
+ point1 = null;
+ }
+
+
+ public void mouseMoved(MouseEvent e){}
+ public void mouseClicked(MouseEvent e){}
+ public void mouseExited(MouseEvent e){}
+ public void mouseEntered(MouseEvent e){}
+
+ private FigureElement findFigureElement(int x, int y) {
+ Point2D p = new Point2D.Float((float)x, (float)y);
+ for (Iterator i = canvas.members(); i.hasNext(); ) {
+ FigureElement fe = (FigureElement)i.next();
+ if (fe.contains(p)) return fe;
+ }
+ return null;
+ }
+ }
+}
--- /dev/null
+/*
+Copyright (c) 2001-2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures.gui;
+
+import figures.support.Log;
+
+aspect LogAdapter {
+
+ before(String s): call(void Log.log(String)) && args(s) {
+ if (Main.panel != null) {
+ Main.panel.cp.println(s);
+ }
+ }
+}
--- /dev/null
+/*
+Copyright (c) 2001-2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures.gui;
+
+import javax.swing.*;
+import figures.support.Log;
+import figures.Point;
+
+public class Main {
+ static FigurePanel panel;
+
+ public static void main(String[] args) {
+ JFrame figureFrame = new JFrame("Figure Editor");
+ panel = new FigurePanel();
+ figureFrame.setContentPane(panel);
+ figureFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ figureFrame.pack();
+ figureFrame.setVisible(true);
+
+ Point p = new Point(0, 0);
+ p.setX(-10);
+ }
+}
--- /dev/null
+package figures.support;\r
+\r
+import figures.*;\r
+\r
+public aspect HistoryUpdating {\r
+\r
+ pointcut moves(FigureElement fe):\r
+ this(fe) &&\r
+ execution(void FigureElement+.set*(..));\r
+ \r
+ after(FigureElement fe) returning: moves(fe) {\r
+ Canvas.updateHistory(fe);\r
+ }\r
+ \r
+ declare error:\r
+ set(private * FigureElement+.*) &&\r
+ !(withincode(void FigureElement+.set*(..)) ||\r
+ withincode(FigureElement+.new(..))): \r
+ "doh!!!";\r
+ \r
+ before(int newValue): \r
+ set(int Point.*) && args(newValue) {\r
+ if (newValue < 0) {\r
+ throw new IllegalArgumentException("too small");\r
+ } \r
+ }\r
+}\r
--- /dev/null
+/*
+Copyright (c) 2002 Palo Alto Research Center Incorporated. All Rights Reserved.
+ */
+
+package figures.support;
+
+public class Log {
+ private static StringBuffer data = new StringBuffer();
+
+ public static void traceObject(Object o) {
+ throw new UnsupportedOperationException();
+ }
+
+ public static void log(String s) {
+ data.append(s);
+ data.append(';');
+ }
+
+ public static void logClassName(Class _class) {
+ String name = _class.getName();
+ int dot = name.lastIndexOf('.');
+ if (dot == -1) {
+ log(name);
+ } else {
+ log(name.substring(dot+1, name.length()));
+ }
+ }
+
+ public static String getString() {
+ return data.toString();
+ }
+
+ public static void clear() {
+ data.setLength(0);
+ }
+}
--- /dev/null
+
+The following folders can be checked out as Eclipse projects:
+
+* figures: refactoring a simple modularity problem in a figure editor
+
+* spacewar: pluggable debug aspect is good for showing build configurations
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="lib" path="C:/AspectJ-Demo/apps/eclipse-3.0/plugins/org.aspectj.ajde_1.1.4/aspectjrt.jar"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>spacewar</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.ajdt.ui.ajbuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.ajdt.ui.ajnature</nature>
+ </natures>
+</projectDescription>
--- /dev/null
+src/coordination/Condition.java
+src/coordination/CoordinationAction.java
+src/coordination/Coordinator.java
+src/coordination/Exclusion.java
+src/coordination/MethodState.java
+src/coordination/Mutex.java
+src/coordination/Selfex.java
+src/coordination/TimeoutException.java
+src/spacewar/Bullet.java
+src/spacewar/Debug.java
+src/spacewar/Display.java
+src/spacewar/Display1.java
+src/spacewar/Display2.java
+src/spacewar/EnergyPacket.java
+src/spacewar/EnergyPacketProducer.java
+src/spacewar/EnsureShipIsAlive.java
+src/spacewar/Game.java
+src/spacewar/GameSynchronization.java
+src/spacewar/Pilot.java
+src/spacewar/Player.java
+src/spacewar/Registry.java
+src/spacewar/RegistrySynchronization.java
+src/spacewar/Robot.java
+src/spacewar/SWFrame.java
+src/spacewar/Ship.java
+src/spacewar/SpaceObject.java
+src/spacewar/Timer.java
--- /dev/null
+
+<head>
+<style>
+<!--
+ table.MsoNormalTable
+ {mso-style-parent:"";
+ font-size:10.0pt;
+ font-family:"Times New Roman";
+ }
+-->
+</style>
+<STYLE TYPE="text/css">
+<!--
+
+ /* FOR THE SDA PAGE */
+
+ /*
+ BODY {margin-top: 15px; margin-left: 15px; margin-right: 15px;}
+ */
+
+ A:link {
+ color:#4756AC;
+ }
+ A:visited {
+ color:#60657B;
+ }
+ A:hover {
+ color:red
+ }
+
+ INPUT {font:12px "Courier New", sans-serif;}
+
+ H2 {
+ font:18px/18px Verdana, Arial, Helvetica, sans-serif;
+ color:black;
+ font-weight:bold;
+ margin-left: 10px;
+ line-height:110%;
+ }
+ H3 {
+ font:18px/18px Verdana, Arial, Helvetica, sans-serif;
+ color:black;
+ font-weight:bold;
+ margin-left: 10px;
+ line-height:110%;
+ }
+ H4 {
+ font:15px/16px Verdana, Arial, Helvetica, sans-serif;
+ color:black;
+ font-weight:bold;
+ margin-left: 10px;
+ line-height:140%;
+ }
+ P {
+ font:13px/13px Verdana, Arial, Helvetica, sans-serif;
+ margin-right: 10px;
+ margin-left: 10px;
+ line-height:130%;
+ }
+ .paragraph {
+ font:13px/13px Verdana, Arial, Helvetica, sans-serif;
+ margin-right: 10px;
+ margin-left: 10px;
+ line-height:130%;
+ }
+ .smallParagraph {
+ font:11px/11px Verdana, Arial, Helvetica, sans-serif;
+ margin-right: 10px;
+ margin-left: 10px;
+ line-height:130%;
+ }
+ LI {
+ font:13px/13px Verdana, Arial, Helvetica, sans-serif;
+ text-align:justify;
+ margin-right: 10px;
+ margin-left: 15px;
+ line-height:120%;
+ }
+ /*
+ UL {
+ font:13px/13px Verdana, Arial, Helvetica, sans-serif;
+ text-align:justify;
+ margin-right: 10px;
+ margin-left: 15px;
+ line-height:120%;
+ }*/
+
+ DL {
+ font:13px/13px Verdana, Arial, Helvetica, sans-serif;
+ text-align:justify;
+ margin-right: 10px;
+ margin-left: 15px;
+ line-height:120%;
+ }
+ B { font:13px/13px Verdana, Arial, Helvetica, sans-serif;
+ font-weight:bold;
+ line-height:140%;
+ }
+ .footer {
+ font:10px/10px Verdana, Arial, Helvetica, sans-serif;
+ color:#888888;
+ text-align:left
+ }
+ .figureTitle {
+ font:13px/13px Verdana, Arial, Helvetica, sans-serif;
+ text-align:justify;
+ text-align:center
+ }
+ .copyrightNotice {
+ font:10px/10px Verdana, Arial, Helvetica, sans-serif;
+ color:#999999;
+ line-height:110%;
+ }
+ .smallHeading {
+ font:13px/13px Verdana, Arial, Helvetica, sans-serif;
+ font-weight:bold;
+ line-height:110%;
+ }
+ .tinyHeading {
+ font:11px/11px Verdana, Arial, Helvetica, sans-serif;
+ font-weight:bold;
+ line-height:120%;
+ }
+ .newsText {
+ font:11px/11px Verdana, Arial, Helvetica, sans-serif;
+ line-height:130%;
+ }
+ .smallParagraph {
+ font:11px/11px Verdana, Arial, Helvetica, sans-serif;
+ line-height:130%;
+ }
+ .fancyHeading {
+ font:20px/20px Chantilly, Arial, Helvetica, sans-serif;
+ margin-right: 10px;
+ color:#6f7a92;
+ margin-left: 10px;
+ line-height:130%;
+ }
+
+-->
+</STYLE>
+</head>
+
+<h2 align="center">AspectJ Spacewar Demo Instructions</h2>
+<h4>Script</h4>
+<p>Many possible things to show here, the following focuses on pluggable aspects
+and build configurations:</p>
+<ul>
+ <li>World\92s first fully interactive video game, now 41 years old.</li>
+ <li>Explain spacewar </li>
+ <li>Launch with Ship.java and "Release.lst" selected and compiled. </li>
+ <li>Run </li>
+ <li>Show debug configuration in config designer</li>
+ <li>Select "Debug.lst", compile, and run again </li>
+ <li>Show debug window, menu, and method tracing</li>
+</ul>
+
--- /dev/null
+src/coordination/Condition.java
+src/coordination/CoordinationAction.java
+src/coordination/Coordinator.java
+src/coordination/Exclusion.java
+src/coordination/MethodState.java
+src/coordination/Mutex.java
+src/coordination/Selfex.java
+src/coordination/TimeoutException.java
+src/spacewar/Bullet.java
+src/spacewar/Display.java
+src/spacewar/Display1.java
+src/spacewar/Display2.java
+src/spacewar/EnergyPacket.java
+src/spacewar/EnergyPacketProducer.java
+src/spacewar/EnsureShipIsAlive.java
+src/spacewar/Game.java
+src/spacewar/GameSynchronization.java
+src/spacewar/Pilot.java
+src/spacewar/Player.java
+src/spacewar/Registry.java
+src/spacewar/RegistrySynchronization.java
+src/spacewar/Robot.java
+src/spacewar/SWFrame.java
+src/spacewar/Ship.java
+src/spacewar/SpaceObject.java
+src/spacewar/Timer.java
--- /dev/null
+/* -*- 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();
+}
--- /dev/null
+/* -*- 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();
+}
--- /dev/null
+/* -*- 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);
+ }
+ }
+}
+
--- /dev/null
+/* -*- 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();
+}
+
--- /dev/null
+/* -*- 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);
+ }
+
+}
--- /dev/null
+/* -*- 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();
+ }
+}
--- /dev/null
+/* -*- 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);
+ }
+
+}
--- /dev/null
+/* -*- 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;
+ }
+}
--- /dev/null
+/*
+
+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();
+ }
+}
--- /dev/null
+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 Ship.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);
+ }
+}
--- /dev/null
+/*
+
+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;
+import java.awt.event.KeyListener;
+
+/**
+ * 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();
+ }
+ }
+ }
+}
--- /dev/null
+/*
+
+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.util.Vector;
+import java.util.Enumeration;
+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);
+ }
+ }
+}
--- /dev/null
+/*
+
+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.util.Vector;
+import java.util.Enumeration;
+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);
+ }
+ }
+}
--- /dev/null
+/*
+
+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();
+ }
+}
--- /dev/null
+/*
+
+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);
+ }
+}
--- /dev/null
+/*
+
+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);
+ }
+ }
+}
--- /dev/null
+/*
+
+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);
+ }
+}
--- /dev/null
+/*
+
+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"});
+ }
+
+}
--- /dev/null
+/*
+
+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;
+ }
+}
--- /dev/null
+/*
+
+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.util.Enumeration;
+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;
+ }
+}
--- /dev/null
+/*
+
+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();
+ }
+ }
+}
+
--- /dev/null
+/*
+
+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"});
+ }
+
+}
--- /dev/null
+/*
+
+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) {}
+ }
+}
--- /dev/null
+/*
+
+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();
+ }
+ }
+}
--- /dev/null
+/*
+
+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()) );
+
+ 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();
+ }
+ }
+
+
+
+ /**
+ * 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;
+
+}
--- /dev/null
+/*
+
+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);
+}
--- /dev/null
+/*
+
+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) { }
+ }
+ }
+ }
+}