diff options
author | mkersten <mkersten> | 2003-11-10 19:11:18 +0000 |
---|---|---|
committer | mkersten <mkersten> | 2003-11-10 19:11:18 +0000 |
commit | ebfe0a1a9adfe64e41349167d4e3b5b188512d58 (patch) | |
tree | 188d7763530ed735b65875a302d5dfdd7ddcc7f4 /docs/teaching | |
parent | d54f21bbd778d29fd2cd1bdbe14402aa7a15551e (diff) | |
download | aspectj-ebfe0a1a9adfe64e41349167d4e3b5b188512d58.tar.gz aspectj-ebfe0a1a9adfe64e41349167d4e3b5b188512d58.zip |
Adding scrubbed demos (figures & spacewar), scripts, slides, and Eclipse project configurations.
Diffstat (limited to 'docs/teaching')
66 files changed, 4978 insertions, 0 deletions
diff --git a/docs/teaching/demos/figures/.classpath b/docs/teaching/demos/figures/.classpath new file mode 100644 index 000000000..8ae844dbf --- /dev/null +++ b/docs/teaching/demos/figures/.classpath @@ -0,0 +1,7 @@ +<?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> diff --git a/docs/teaching/demos/figures/.project b/docs/teaching/demos/figures/.project new file mode 100644 index 000000000..ccc6028ef --- /dev/null +++ b/docs/teaching/demos/figures/.project @@ -0,0 +1,19 @@ +<?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> diff --git a/docs/teaching/demos/figures/build.xml b/docs/teaching/demos/figures/build.xml new file mode 100644 index 000000000..2816008dc --- /dev/null +++ b/docs/teaching/demos/figures/build.xml @@ -0,0 +1,15 @@ +<?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 diff --git a/docs/teaching/demos/figures/readme.html b/docs/teaching/demos/figures/readme.html new file mode 100644 index 000000000..49e59396e --- /dev/null +++ b/docs/teaching/demos/figures/readme.html @@ -0,0 +1,299 @@ + +<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’s 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 “on the way back out”)</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> diff --git a/docs/teaching/demos/figures/scratch/HistoryUpdating.java b/docs/teaching/demos/figures/scratch/HistoryUpdating.java new file mode 100644 index 000000000..f4eff3d3f --- /dev/null +++ b/docs/teaching/demos/figures/scratch/HistoryUpdating.java @@ -0,0 +1,27 @@ +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"); + } + } +} diff --git a/docs/teaching/demos/figures/src-fresh/figures/Box.java b/docs/teaching/demos/figures/src-fresh/figures/Box.java new file mode 100644 index 000000000..4db7f439d --- /dev/null +++ b/docs/teaching/demos/figures/src-fresh/figures/Box.java @@ -0,0 +1,55 @@ +/* +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()); + } +} + diff --git a/docs/teaching/demos/figures/src-fresh/figures/Canvas.java b/docs/teaching/demos/figures/src-fresh/figures/Canvas.java new file mode 100644 index 000000000..ef849a5d5 --- /dev/null +++ b/docs/teaching/demos/figures/src-fresh/figures/Canvas.java @@ -0,0 +1,15 @@ +/* +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); + } +} diff --git a/docs/teaching/demos/figures/src-fresh/figures/ColorControl.java b/docs/teaching/demos/figures/src-fresh/figures/ColorControl.java new file mode 100644 index 000000000..46d1ba428 --- /dev/null +++ b/docs/teaching/demos/figures/src-fresh/figures/ColorControl.java @@ -0,0 +1,20 @@ +/* +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 +} diff --git a/docs/teaching/demos/figures/src-fresh/figures/FigureElement.java b/docs/teaching/demos/figures/src-fresh/figures/FigureElement.java new file mode 100644 index 000000000..ae06c132b --- /dev/null +++ b/docs/teaching/demos/figures/src-fresh/figures/FigureElement.java @@ -0,0 +1,21 @@ +/* +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); +} diff --git a/docs/teaching/demos/figures/src-fresh/figures/Group.java b/docs/teaching/demos/figures/src-fresh/figures/Group.java new file mode 100644 index 000000000..59c1a17cf --- /dev/null +++ b/docs/teaching/demos/figures/src-fresh/figures/Group.java @@ -0,0 +1,88 @@ +/* +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(); + } +} + diff --git a/docs/teaching/demos/figures/src-fresh/figures/Line.java b/docs/teaching/demos/figures/src-fresh/figures/Line.java new file mode 100644 index 000000000..e76206639 --- /dev/null +++ b/docs/teaching/demos/figures/src-fresh/figures/Line.java @@ -0,0 +1,70 @@ +/* +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(); + } +} + diff --git a/docs/teaching/demos/figures/src-fresh/figures/Point.java b/docs/teaching/demos/figures/src-fresh/figures/Point.java new file mode 100644 index 000000000..9699aaf31 --- /dev/null +++ b/docs/teaching/demos/figures/src-fresh/figures/Point.java @@ -0,0 +1,59 @@ +/* +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); + } +} + diff --git a/docs/teaching/demos/figures/src-fresh/figures/ShapeFigureElement.java b/docs/teaching/demos/figures/src-fresh/figures/ShapeFigureElement.java new file mode 100644 index 000000000..29a4a89ad --- /dev/null +++ b/docs/teaching/demos/figures/src-fresh/figures/ShapeFigureElement.java @@ -0,0 +1,38 @@ +/* +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); + } +} diff --git a/docs/teaching/demos/figures/src-fresh/figures/SlothfulPoint.java b/docs/teaching/demos/figures/src-fresh/figures/SlothfulPoint.java new file mode 100644 index 000000000..620970373 --- /dev/null +++ b/docs/teaching/demos/figures/src-fresh/figures/SlothfulPoint.java @@ -0,0 +1,42 @@ +/* +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); + } +} + diff --git a/docs/teaching/demos/figures/src-fresh/figures/gui/FigurePanel.java b/docs/teaching/demos/figures/src-fresh/figures/gui/FigurePanel.java new file mode 100644 index 000000000..cac59e835 --- /dev/null +++ b/docs/teaching/demos/figures/src-fresh/figures/gui/FigurePanel.java @@ -0,0 +1,172 @@ +/* +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; + } + } +} diff --git a/docs/teaching/demos/figures/src-fresh/figures/gui/LogAdapter.java b/docs/teaching/demos/figures/src-fresh/figures/gui/LogAdapter.java new file mode 100644 index 000000000..92a192f15 --- /dev/null +++ b/docs/teaching/demos/figures/src-fresh/figures/gui/LogAdapter.java @@ -0,0 +1,16 @@ +/* +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); + } + } +} diff --git a/docs/teaching/demos/figures/src-fresh/figures/gui/Main.java b/docs/teaching/demos/figures/src-fresh/figures/gui/Main.java new file mode 100644 index 000000000..da257aeda --- /dev/null +++ b/docs/teaching/demos/figures/src-fresh/figures/gui/Main.java @@ -0,0 +1,25 @@ +/* +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); + } +} diff --git a/docs/teaching/demos/figures/src-fresh/figures/support/Log.java b/docs/teaching/demos/figures/src-fresh/figures/support/Log.java new file mode 100644 index 000000000..1ae18cc46 --- /dev/null +++ b/docs/teaching/demos/figures/src-fresh/figures/support/Log.java @@ -0,0 +1,36 @@ +/* +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); + } +} diff --git a/docs/teaching/demos/figures/src/figures/Box.java b/docs/teaching/demos/figures/src/figures/Box.java new file mode 100644 index 000000000..4db7f439d --- /dev/null +++ b/docs/teaching/demos/figures/src/figures/Box.java @@ -0,0 +1,55 @@ +/* +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()); + } +} + diff --git a/docs/teaching/demos/figures/src/figures/Canvas.java b/docs/teaching/demos/figures/src/figures/Canvas.java new file mode 100644 index 000000000..ef849a5d5 --- /dev/null +++ b/docs/teaching/demos/figures/src/figures/Canvas.java @@ -0,0 +1,15 @@ +/* +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); + } +} diff --git a/docs/teaching/demos/figures/src/figures/ColorControl.java b/docs/teaching/demos/figures/src/figures/ColorControl.java new file mode 100644 index 000000000..f1a76512b --- /dev/null +++ b/docs/teaching/demos/figures/src/figures/ColorControl.java @@ -0,0 +1,16 @@ +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 +} diff --git a/docs/teaching/demos/figures/src/figures/FigureElement.java b/docs/teaching/demos/figures/src/figures/FigureElement.java new file mode 100644 index 000000000..ae06c132b --- /dev/null +++ b/docs/teaching/demos/figures/src/figures/FigureElement.java @@ -0,0 +1,21 @@ +/* +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); +} diff --git a/docs/teaching/demos/figures/src/figures/Group.java b/docs/teaching/demos/figures/src/figures/Group.java new file mode 100644 index 000000000..59c1a17cf --- /dev/null +++ b/docs/teaching/demos/figures/src/figures/Group.java @@ -0,0 +1,88 @@ +/* +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(); + } +} + diff --git a/docs/teaching/demos/figures/src/figures/Line.java b/docs/teaching/demos/figures/src/figures/Line.java new file mode 100644 index 000000000..e9911c6d7 --- /dev/null +++ b/docs/teaching/demos/figures/src/figures/Line.java @@ -0,0 +1,70 @@ +/* +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(); + } +} + diff --git a/docs/teaching/demos/figures/src/figures/Point.java b/docs/teaching/demos/figures/src/figures/Point.java new file mode 100644 index 000000000..d832b983c --- /dev/null +++ b/docs/teaching/demos/figures/src/figures/Point.java @@ -0,0 +1,59 @@ +/* +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); + } +} + diff --git a/docs/teaching/demos/figures/src/figures/ShapeFigureElement.java b/docs/teaching/demos/figures/src/figures/ShapeFigureElement.java new file mode 100644 index 000000000..29a4a89ad --- /dev/null +++ b/docs/teaching/demos/figures/src/figures/ShapeFigureElement.java @@ -0,0 +1,38 @@ +/* +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); + } +} diff --git a/docs/teaching/demos/figures/src/figures/SlothfulPoint.java b/docs/teaching/demos/figures/src/figures/SlothfulPoint.java new file mode 100644 index 000000000..620970373 --- /dev/null +++ b/docs/teaching/demos/figures/src/figures/SlothfulPoint.java @@ -0,0 +1,42 @@ +/* +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); + } +} + diff --git a/docs/teaching/demos/figures/src/figures/gui/FigurePanel.java b/docs/teaching/demos/figures/src/figures/gui/FigurePanel.java new file mode 100644 index 000000000..cac59e835 --- /dev/null +++ b/docs/teaching/demos/figures/src/figures/gui/FigurePanel.java @@ -0,0 +1,172 @@ +/* +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; + } + } +} diff --git a/docs/teaching/demos/figures/src/figures/gui/LogAdapter.java b/docs/teaching/demos/figures/src/figures/gui/LogAdapter.java new file mode 100644 index 000000000..92a192f15 --- /dev/null +++ b/docs/teaching/demos/figures/src/figures/gui/LogAdapter.java @@ -0,0 +1,16 @@ +/* +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); + } + } +} diff --git a/docs/teaching/demos/figures/src/figures/gui/Main.java b/docs/teaching/demos/figures/src/figures/gui/Main.java new file mode 100644 index 000000000..d69f903e8 --- /dev/null +++ b/docs/teaching/demos/figures/src/figures/gui/Main.java @@ -0,0 +1,25 @@ +/* +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); + } +} diff --git a/docs/teaching/demos/figures/src/figures/support/HistoryUpdating.aj b/docs/teaching/demos/figures/src/figures/support/HistoryUpdating.aj new file mode 100644 index 000000000..9699d032a --- /dev/null +++ b/docs/teaching/demos/figures/src/figures/support/HistoryUpdating.aj @@ -0,0 +1,27 @@ +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");
+ }
+ }
+}
diff --git a/docs/teaching/demos/figures/src/figures/support/Log.java b/docs/teaching/demos/figures/src/figures/support/Log.java new file mode 100644 index 000000000..1ae18cc46 --- /dev/null +++ b/docs/teaching/demos/figures/src/figures/support/Log.java @@ -0,0 +1,36 @@ +/* +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); + } +} diff --git a/docs/teaching/demos/oopsla2003-mik.ppt b/docs/teaching/demos/oopsla2003-mik.ppt Binary files differnew file mode 100644 index 000000000..4c89e0a45 --- /dev/null +++ b/docs/teaching/demos/oopsla2003-mik.ppt diff --git a/docs/teaching/demos/readme.txt b/docs/teaching/demos/readme.txt new file mode 100644 index 000000000..24b267deb --- /dev/null +++ b/docs/teaching/demos/readme.txt @@ -0,0 +1,6 @@ + +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 diff --git a/docs/teaching/demos/spacewar/.classpath b/docs/teaching/demos/spacewar/.classpath new file mode 100644 index 000000000..8ae844dbf --- /dev/null +++ b/docs/teaching/demos/spacewar/.classpath @@ -0,0 +1,7 @@ +<?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> diff --git a/docs/teaching/demos/spacewar/.project b/docs/teaching/demos/spacewar/.project new file mode 100644 index 000000000..4fbdcbb2b --- /dev/null +++ b/docs/teaching/demos/spacewar/.project @@ -0,0 +1,18 @@ +<?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> diff --git a/docs/teaching/demos/spacewar/debug.lst b/docs/teaching/demos/spacewar/debug.lst new file mode 100644 index 000000000..fc161a751 --- /dev/null +++ b/docs/teaching/demos/spacewar/debug.lst @@ -0,0 +1,27 @@ +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 diff --git a/docs/teaching/demos/spacewar/readme.html b/docs/teaching/demos/spacewar/readme.html new file mode 100644 index 000000000..7b1a75e3e --- /dev/null +++ b/docs/teaching/demos/spacewar/readme.html @@ -0,0 +1,157 @@ + +<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’s 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> + diff --git a/docs/teaching/demos/spacewar/release.lst b/docs/teaching/demos/spacewar/release.lst new file mode 100644 index 000000000..a6b75a000 --- /dev/null +++ b/docs/teaching/demos/spacewar/release.lst @@ -0,0 +1,26 @@ +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 diff --git a/docs/teaching/demos/spacewar/src/coordination/Condition.java b/docs/teaching/demos/spacewar/src/coordination/Condition.java new file mode 100644 index 000000000..18bbafee2 --- /dev/null +++ b/docs/teaching/demos/spacewar/src/coordination/Condition.java @@ -0,0 +1,37 @@ +/* -*- Mode: Java; -*- + +Copyright (c) Xerox Corporation 1998-2002. All rights reserved. + +Use and copying of this software and preparation of derivative works based +upon this software are permitted. Any distribution of this software or +derivative works must comply with all applicable United States export control +laws. + +This software is made available AS IS, and Xerox Corporation makes no warranty +about the software, its performance or its conformity to any specification. + +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| + +*/ + +package coordination; + + +/** + * Interface for pre-conditions that are passed to guardedEntry methods of + * Coordinator. + * Conditions should be passed as anonymous classes that simply implement + * the checkit method. + * + */ +public interface Condition { + + /** + * This method is called automatically by Coordinator.guardedEntry(...) + * and it's called everytime the coordination state changes. + */ + + public boolean checkit(); +} diff --git a/docs/teaching/demos/spacewar/src/coordination/CoordinationAction.java b/docs/teaching/demos/spacewar/src/coordination/CoordinationAction.java new file mode 100644 index 000000000..7825b95b7 --- /dev/null +++ b/docs/teaching/demos/spacewar/src/coordination/CoordinationAction.java @@ -0,0 +1,37 @@ +/* -*- Mode: Java; -*- + +Copyright (c) Xerox Corporation 1998-2002. All rights reserved. + +Use and copying of this software and preparation of derivative works based +upon this software are permitted. Any distribution of this software or +derivative works must comply with all applicable United States export control +laws. + +This software is made available AS IS, and Xerox Corporation makes no warranty +about the software, its performance or its conformity to any specification. + +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| + +*/ + +package coordination; + + +/** + * Interface for coordination actions that are passed to guardedEntry methods of + * Coordinator. + * Coordination actions should be passed as anonymous classes that simply + * implement the doit method. + * + */ +public interface CoordinationAction { + /** + * This method is called by Coordinator.guardedEntry(...) and + * Coordinator.guardedExit(...). Use it for changing coordination state + * upon entering and exiting methods. + */ + + public void doit(); +} diff --git a/docs/teaching/demos/spacewar/src/coordination/Coordinator.java b/docs/teaching/demos/spacewar/src/coordination/Coordinator.java new file mode 100644 index 000000000..ea0522d6b --- /dev/null +++ b/docs/teaching/demos/spacewar/src/coordination/Coordinator.java @@ -0,0 +1,449 @@ +/* -*- Mode: Java; -*- + +Copyright (c) Xerox Corporation 1998-2002. All rights reserved. + +Use and copying of this software and preparation of derivative works based +upon this software are permitted. Any distribution of this software or +derivative works must comply with all applicable United States export control +laws. + +This software is made available AS IS, and Xerox Corporation makes no warranty +about the software, its performance or its conformity to any specification. + +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| + +*/ + +package coordination; + +import java.util.*; //!!! + +/** + * The Coordinator class provides the basic functionality for synchronizing + * and coordinating different threads upon entering and exiting methods. + * It can be used in two different ways: + * 1) by instantiating regular coordinator objects that are used by aspects; or + * 2) by extending it (sub-classing) with coordinator aspects. + * <P> + * Method invocations are the smallest units for defining critical sections + * and pre-conditions. The use of coordinators, either regular objects or aspect + * instances, should always end up by invoking guardedEntry(...) in a + * before weave and guardedExit(...) in an after weave for all methods that + * need coordination. guardedEntry and guardedExit are the methods that + * actually manage the synchronization and coordination constraints given + * by their parameters and by pre-existent exclusion markers. + * <P> + * The synchronization of threads for the execution of critical section + * methods in an object is done by marking those methods as self- and/or + * mutually-exclusive (addSelfex, addMutex). + * Just by itself, addSelfex("M") does not enforce the self-exclusion + * of method M - enforcement is done by invoking guardedEntry before + * M is executed. Similarly, addMutex(new String[] {"M1", "M2"}) does + * not enforce the mutual exclusion between methods M1 and M2. + * <P> + * A guardedEntry on a method that has been marked as self-exclusive + * ensures that the method is executed in the invoked object by only one thread + * at a time. A guardedEntry on a method that has been marked has mutually- + * exclusive with other methods ensures that the execution of that method + * by a thread in the invoked object temporarily blocks the execution by + * other threads of the methods that are in the same mutex set. + * <P> + * The coordination of threads, i.e. their explicit suspension and + * resumption, is done through the use of pre-conditions and coordination + * actions that are passed as parameters to guardedEntry and guardedExit + * with the form of anonymous classes. + */ +public abstract aspect Coordinator { + private Hashtable methods = null; + private Vector exclusions = null; + + abstract protected pointcut synchronizationPoint(); + + public Coordinator() { + methods = new Hashtable(); + exclusions = new Vector(5); + } + + before (): synchronizationPoint() { + this.guardedEntry(thisJoinPointStaticPart.getSignature().getName()); + } + + after (): synchronizationPoint() { + this.guardedExit(thisJoinPointStaticPart.getSignature().getName()); + } + + /** + * Takes a multi-part method name (eg "BoundedBuffer.put") + * and marks that method as self-exclusive. + * No checks are made with respect to the existence of the method + * whose name is given. + */ + public synchronized void addSelfex(String methName) { + Selfex sex = new Selfex (methName); + + // update db of all exclusions in this coordinator + exclusions.addElement(sex); + + // update local info in method + Method aMeth = getOrSetMethod(methName); + aMeth.addExclusion(sex); + } + + /** + * Takes a multi-part method name (e.g. "BoundedBuffer.put") + * and removes that method from the list of self-exclusive methods. + */ + public synchronized void removeSelfex(String methName) { + for (int i = 0; i < exclusions.size(); i++) { + Exclusion sex = (Exclusion)exclusions.elementAt(i); + if ((sex instanceof Selfex) && + (((Selfex)sex).methodName.equals(methName))) { + + // update db of all exclusions in this coordinator + exclusions.removeElementAt(i); + + // update local info in method + Method aMeth = getOrSetMethod(methName); + aMeth.removeExclusion(sex); + } + } + } + + /** + * Takes an array of multi-part method names and marks those + * methods as mutually exclusive. + * No checks are made with respect to the existence of the methods + * whose names are given. + */ + public synchronized void addMutex(String[] methNames) { + Mutex mux = new Mutex(methNames); + + // update db of all exclusions in this coordinator + exclusions.addElement(mux); + + // update local info in each method + for (int i = 0; i < methNames.length; i++) { + Method aMeth = getOrSetMethod(methNames[i]); + aMeth.addExclusion(mux); + } + } + + /** + * Takes an array of multi-part method names that correspond + * to an existing mutex set and remove the mutual exclusion constraint. + * If the given mutex set does not exist, removeMutex does nothing. + */ + public synchronized void removeMutex(String[] methNames) { + for (int i = 0; i < exclusions.size(); i++) { + Exclusion mux = (Exclusion)exclusions.elementAt(i); + if (mux instanceof Mutex) { + boolean same = true; + for (int j = 0; j < methNames.length; j++) + if (!methNames[j].equals(((Mutex)mux).methodNames[j])) + same = false; + if (same) { + // update db of all exclusions in this coordinator + exclusions.removeElementAt(i); + + // update local info in each method involved + for (int j = 0; j < methNames.length; j++) { + Method aMeth = getOrSetMethod(methNames[j]); + aMeth.removeExclusion(mux); + } + } + } + } + } + + /** + * This method is the guard for enforcing all synchronization and + * coordination constraints of a given method, and it should be called + * just before the method is executed. + * In this form, only the method name is given. The only constraints + * checked are the exclusion constraints. + * If the method was previousely marked as selfex (through addSelfex), + * guardedEntry ensures that the method is executed only when no other + * thread is executing it. + * If the method was previousely marked as being in one or more mutex + * sets, guardedEntry ensures that the method is executed only when no other + * thread is executing any of the methods with which the give method is + * mutexed. + */ + public synchronized void guardedEntry(String methName) { + guardedEntry(methName, new Condition() { + public boolean checkit() { + return true; + } + }, null); + } + + /** + * Just like guardedEntry(String methName), but the given method is executed + * only when the given condition is true. + * guardedEntry is the guard for enforcing all synchronization and + * coordination constraints of a given method, and it should be called + * just before the method is executed. + * In this form, the method name is given along with a condition. + * The constraints checked are the exclusion constraints and whether + * the given condition is true. + * If the method was previousely marked as selfex (through addSelfex), + * guardedEntry ensures that the method is executed only when no other + * thread is executing it. + * If the method was previousely marked as being in one or more mutex + * sets, guardedEntry ensures that the method is executed only when no other + * thread is executing any of the methods with which the give method is + * mutexed. + * If the condition is false, guardedEntry suspends the current thread. + * That thread remains suspended until the condition becomes true, in + * which case all constraints are rechecked before the method is executed. + * When all exclusion constraints are checked and the given condition is + * true, the given method is executed. + */ + public synchronized void guardedEntry(String methName, Condition condition) { + guardedEntry(methName, condition, null); + } + + /** + * Just like guardedEntry(String methName), but with an additional + * coordination action that is executed before the given method is + * executed. + * guardedEntry is the guard for enforcing all synchronization and + * coordination constraints of a given method, and it should be called + * just before the method is executed. + * In this form, the method name is given along with a coordination action. + * The only constraints checked are the exclusion constraints. + * If the method was previousely marked as selfex (through addSelfex), + * guardedEntry ensures that the method is executed only when no other + * thread is executing it. + * If the method was previousely marked as being in one or more mutex + * sets, guardedEntry ensures that the method is executed only when no other + * thread is executing any of the methods with which the give method is + * mutexed. + * The given coordination action is executed just before the given method + * is executed. + */ + public synchronized void guardedEntry(String methName, + CoordinationAction action) { + guardedEntry(methName, new Condition() { + public boolean checkit() { + return true; + } + }, + action); + } + + /** + * Just like guardedEntry(String methName), but the given method is executed + * only when the given condition is true; the additional + * coordination action that is executed before the given method is + * executed. + * guardedEntry is the guard for enforcing all synchronization and + * coordination constraints of a given method, and it should be called + * just before the method is executed. + * In this form, the method name is given along with a condition and + * a coordination action. + * The constraints checked are the exclusion constraints and whether the + * given condition is true. + * If the method was previousely marked as selfex (through addSelfex), + * guardedEntry ensures that the method is executed only when no other + * thread is executing it. + * If the method was previousely marked as being in one or more mutex + * sets, guardedEntry ensures that the method is executed only when no other + * thread is executing any of the methods with which the give method is + * mutexed. + * If the condition is false, guardedEntry suspends the current thread. + * That thread remains suspended until the condition becomes true, in + * which case all constraints are rechecked before the method is executed. + * When all exclusion constraints are checked and the given condition is + * true, the given method is executed. + * The given coordination action is executed just before the given method + * is executed. + */ + public synchronized void guardedEntry(String methName, + Condition condition, + CoordinationAction action) { + Method aMeth = getOrSetMethod(methName); + boolean canGo = false; + + // test pre-conditions for entering the method + while (!canGo) { + canGo = true; + for (int i = 0; i < aMeth.exes.size() && canGo; i++) + if (!((Exclusion)aMeth.exes.elementAt(i)).testExclusion(aMeth.name)) { + canGo = false; + } + if (canGo && !condition.checkit()) { + canGo = false; + } + if (!canGo) + try { + wait(); + } catch (InterruptedException e) { } + } + + // OK. + enterMethod(aMeth, action); + } + + /** + * This method is similar to guardedEntry, but it takes + * an additional parameter - the milliseconds after which any suspension + * will abort with a timeout. + */ + public synchronized void guardedEntryWithTimeout(String methName, + long millis) + throws TimeoutException { + guardedEntryWithTimeout(methName, new Condition() { + public boolean checkit() { + return true; + } + }, null, millis); + } + + /** + * This method is similar to guardedEntry, but it takes + * an additional parameter - the milliseconds after which any suspension + * will abort with a timeout. + */ + public synchronized void guardedEntryWithTimeout(String methName, + Condition condition, + long millis) + throws TimeoutException { + guardedEntryWithTimeout(methName, condition, null, millis); + } + + /** + * This method is similar to guardedEntry, but it takes + * an additional parameter - the milliseconds after which any suspension + * will abort with a timeout. + */ + public synchronized void guardedEntryWithTimeout(String methName, + CoordinationAction action, + long millis) + throws TimeoutException { + guardedEntryWithTimeout(methName, new Condition() { + public boolean checkit() { + return true; + } + }, action, millis); + } + + /** + * This method is similar to guardedEntry, but it takes + * an additional parameter - the milliseconds after which any suspension + * will abort with a timeout. + */ + public synchronized void guardedEntryWithTimeout(String methName, + Condition condition, + CoordinationAction action, + long millis) + throws TimeoutException { + + Method aMeth = getOrSetMethod(methName); + boolean canGo = false; + long waitTime = millis; + long startTime = System.currentTimeMillis(); + + // test pre-conditions for entering the method + while (!canGo) { + canGo = true; + for (int i = 0; i < aMeth.exes.size() && canGo; i++) + if ((!((Exclusion)aMeth.exes.elementAt(i)).testExclusion(aMeth.name)) || + (!condition.checkit())) { + canGo = false; + } + if (!canGo) { + try { + wait(waitTime); + } catch (InterruptedException e) {} + + long now = System.currentTimeMillis(); + long timeSoFar = now - startTime; + if (timeSoFar >= millis) // timeout! + throw new TimeoutException(timeSoFar); + else // adjust time + waitTime = millis - timeSoFar; + } + } + + // OK. + enterMethod(aMeth, action); + } + + /** + * This method provides the means for updating all synchronization and + * coordination state after the execution of a given method, and it should be + * called after the method is executed. + * In this form, only the method name is given. + * The synchronization state for self- and mutual-exclusion is + * automatically upadted. + */ + public synchronized void guardedExit(String methName) { + guardedExit(methName, null); + } + + /** + * Just like guardedExit(String methName) but with an additional + * coordination action that is executed. + * guardedExit provides the means for updating all synchronization and + * coordination state after the execution of a given method, and it should be + * called after the method is executed. + * In this form, the method name is given along with a coordination action. + * The synchronization state for self- and mutual-exclusion is + * automatically upadted. + * The given coordination action is executed. + */ + public synchronized void guardedExit(String methName, + CoordinationAction action) { + Method aMeth = getOrSetMethod(methName); + + for (int i = 0; i < aMeth.exes.size(); i++) + ((Exclusion)aMeth.exes.elementAt(i)).exitExclusion(methName); + if (action != null) action.doit(); + notifyAll(); + } + + private Method getOrSetMethod(String methName) { + Method aMeth = null; + if (!methods.containsKey(methName)) { + methods.put(methName, (aMeth = new Method(methName))); + } + else { + aMeth = (Method) methods.get(methName); + } + return aMeth; + } + + private void enterMethod(Method aMeth, CoordinationAction action) { + for (int i = 0; i < aMeth.exes.size(); i++) + ((Exclusion)aMeth.exes.elementAt(i)).enterExclusion(aMeth.name); + + if (action != null) action.doit(); + } + + + +} + +class Method { + String name; + Vector exes = new Vector(3); + + Method(String n) { + name = n; + } + + void addExclusion(Exclusion ex) { + exes.addElement(ex); + } + + void removeExclusion(Exclusion ex) { + for (int i = 0; i < exes.size(); i++) { + if (exes.elementAt(i) == ex) + exes.removeElementAt(i); + } + } +} + diff --git a/docs/teaching/demos/spacewar/src/coordination/Exclusion.java b/docs/teaching/demos/spacewar/src/coordination/Exclusion.java new file mode 100644 index 000000000..9179cd6e0 --- /dev/null +++ b/docs/teaching/demos/spacewar/src/coordination/Exclusion.java @@ -0,0 +1,33 @@ +/* -*- Mode: Java; -*- + +Copyright (c) Xerox Corporation 1998-2002. All rights reserved. + +Use and copying of this software and preparation of derivative works based +upon this software are permitted. Any distribution of this software or +derivative works must comply with all applicable United States export control +laws. + +This software is made available AS IS, and Xerox Corporation makes no warranty +about the software, its performance or its conformity to any specification. + +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| + +*/ + +package coordination; + + +interface Exclusion { + + boolean testExclusion(String methodName); + + void enterExclusion(String methodName); + + void exitExclusion(String methodName); + + // for debug !!! + void printNames(); +} + diff --git a/docs/teaching/demos/spacewar/src/coordination/MethodState.java b/docs/teaching/demos/spacewar/src/coordination/MethodState.java new file mode 100644 index 000000000..03a44378a --- /dev/null +++ b/docs/teaching/demos/spacewar/src/coordination/MethodState.java @@ -0,0 +1,45 @@ +/* -*- Mode: Java; -*- + +Copyright (c) Xerox Corporation 1998-2002. All rights reserved. + +Use and copying of this software and preparation of derivative works based +upon this software are permitted. Any distribution of this software or +derivative works must comply with all applicable United States export control +laws. + +This software is made available AS IS, and Xerox Corporation makes no warranty +about the software, its performance or its conformity to any specification. + +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| + +*/ + +package coordination; + +import java.util.Vector; +import java.util.Enumeration; + + +class MethodState { + + Vector threads=new Vector(); + + void enterInThread (Thread t) { + threads.addElement(t); + } + + void exitInThread(Thread t) { + threads.removeElement(t); + } + + boolean hasOtherThreadThan(Thread t) { + Enumeration e = threads.elements(); + while (e.hasMoreElements()) + if (e.nextElement() != t) + return(true); + return (false); + } + +} diff --git a/docs/teaching/demos/spacewar/src/coordination/Mutex.java b/docs/teaching/demos/spacewar/src/coordination/Mutex.java new file mode 100644 index 000000000..2472137c6 --- /dev/null +++ b/docs/teaching/demos/spacewar/src/coordination/Mutex.java @@ -0,0 +1,86 @@ +/* -*- Mode: Java; -*- + +Copyright (c) Xerox Corporation 1998-2002. All rights reserved. + +Use and copying of this software and preparation of derivative works based +upon this software are permitted. Any distribution of this software or +derivative works must comply with all applicable United States export control +laws. + +This software is made available AS IS, and Xerox Corporation makes no warranty +about the software, its performance or its conformity to any specification. + +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| + +*/ + +package coordination; + +import java.lang.String; + + +class Mutex implements Exclusion { + String[] methodNames; + MethodState[] methodStates; + + String prettyName; + + Mutex (String[] _methodNames) { + methodNames = _methodNames; + methodStates = new MethodState[methodNames.length]; + for (int i = 0; i < methodNames.length; i++) { + methodStates[i] = new MethodState(); + } + } + + private boolean isMethodIn (String _methodName) { + for (int i = 0; i < methodNames.length; i++) { + if (_methodName.equals(methodNames[i])) + return(true); + } + return(false); + } + + private MethodState getMethodState (String _methodName) { + for (int i = 0; i < methodNames.length; i++) { + if (_methodName.equals(methodNames[i])) + return(methodStates[i]); + } + return(null); + } + + public boolean testExclusion (String _methodName) { + Thread ct = Thread.currentThread(); + // + // Loop through each of the other methods in this exclusion set, to be sure + // that no other thread is running them. Note that we have to be careful + // about selfex. + // + for (int i = 0; i < methodNames.length; i++) { + if (!_methodName.equals(methodNames[i])) { + if (methodStates[i].hasOtherThreadThan(ct)) + return(false); + } + } + return (true); + } + + public void enterExclusion (String _methodName) { + MethodState methodState = getMethodState(_methodName); + methodState.enterInThread(Thread.currentThread()); + } + + public void exitExclusion (String _methodName) { + MethodState methodState = getMethodState(_methodName); + methodState.exitInThread(Thread.currentThread()); + } + + public void printNames() { + System.out.print("Mutex names: "); + for (int i = 0; i < methodNames.length; i++) + System.out.print(methodNames[i] + " "); + System.out.println(); + } +} diff --git a/docs/teaching/demos/spacewar/src/coordination/Selfex.java b/docs/teaching/demos/spacewar/src/coordination/Selfex.java new file mode 100644 index 000000000..ff73afd61 --- /dev/null +++ b/docs/teaching/demos/spacewar/src/coordination/Selfex.java @@ -0,0 +1,55 @@ +/* -*- Mode: Java; -*- + +Copyright (c) Xerox Corporation 1998-2002. All rights reserved. + +Use and copying of this software and preparation of derivative works based +upon this software are permitted. Any distribution of this software or +derivative works must comply with all applicable United States export control +laws. + +This software is made available AS IS, and Xerox Corporation makes no warranty +about the software, its performance or its conformity to any specification. + +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| + +*/ + +package coordination; + + +import java.lang.String; + +class Selfex implements Exclusion { + String methodName; + Thread thread; + int count = 0; + + Selfex (String _methodName) { + methodName = _methodName; + } + + public boolean testExclusion (String _methodName) { + if (count == 0) + return(true); + return (thread == Thread.currentThread()); + } + + public void enterExclusion (String _methodName) { + count++; + thread = Thread.currentThread(); // note that if count wasn't 0 + // we aren't changing thread + } + + public void exitExclusion (String _methodName) { + count--; + if (count == 0) // not stricly necessary, but... + thread = null; + } + + public void printNames() { + System.out.println("Selfex name: " + methodName); + } + +} diff --git a/docs/teaching/demos/spacewar/src/coordination/TimeoutException.java b/docs/teaching/demos/spacewar/src/coordination/TimeoutException.java new file mode 100644 index 000000000..e16aa7f09 --- /dev/null +++ b/docs/teaching/demos/spacewar/src/coordination/TimeoutException.java @@ -0,0 +1,27 @@ +/* -*- Mode: Java; -*- + +Copyright (c) Xerox Corporation 1998-2002. All rights reserved. + +Use and copying of this software and preparation of derivative works based +upon this software are permitted. Any distribution of this software or +derivative works must comply with all applicable United States export control +laws. + +This software is made available AS IS, and Xerox Corporation makes no warranty +about the software, its performance or its conformity to any specification. + +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| + +*/ + +package coordination; + + +public class TimeoutException extends Exception { + long time; + TimeoutException(long _time) { + time = _time; + } +} diff --git a/docs/teaching/demos/spacewar/src/spacewar/Bullet.java b/docs/teaching/demos/spacewar/src/spacewar/Bullet.java new file mode 100644 index 000000000..6581dbbf7 --- /dev/null +++ b/docs/teaching/demos/spacewar/src/spacewar/Bullet.java @@ -0,0 +1,48 @@ +/* + +Copyright (c) Xerox Corporation 1998-2002. All rights reserved. + +Use and copying of this software and preparation of derivative works based +upon this software are permitted. Any distribution of this software or +derivative works must comply with all applicable United States export control +laws. + +This software is made available AS IS, and Xerox Corporation makes no warranty +about the software, its performance or its conformity to any specification. + +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| + + +Bullet.java +Part of the Spacewar game. + +*/ + +package spacewar; + +class Bullet extends SpaceObject { + + static private final int SIZE = 3; //Can't be changed for now!!! + static private int LIFETIME = 50; + + private int lifeLeft; + + Bullet (Game theGame, double xP, double yP, double xV, double yV) { + super(theGame, xP, yP, xV, yV); + lifeLeft = LIFETIME; + } + + int getSize() { return SIZE; } + + void handleCollision(SpaceObject obj) { + die(); + } + + void clockTick() { + if (--lifeLeft == 0) + die(); + super.clockTick(); + } +} diff --git a/docs/teaching/demos/spacewar/src/spacewar/Debug.java b/docs/teaching/demos/spacewar/src/spacewar/Debug.java new file mode 100644 index 000000000..d22a2f507 --- /dev/null +++ b/docs/teaching/demos/spacewar/src/spacewar/Debug.java @@ -0,0 +1,198 @@ +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); + } +} diff --git a/docs/teaching/demos/spacewar/src/spacewar/Display.java b/docs/teaching/demos/spacewar/src/spacewar/Display.java new file mode 100644 index 000000000..b5b9c9421 --- /dev/null +++ b/docs/teaching/demos/spacewar/src/spacewar/Display.java @@ -0,0 +1,166 @@ +/* + +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(); + } + } + } +} diff --git a/docs/teaching/demos/spacewar/src/spacewar/Display1.java b/docs/teaching/demos/spacewar/src/spacewar/Display1.java new file mode 100644 index 000000000..484a2342a --- /dev/null +++ b/docs/teaching/demos/spacewar/src/spacewar/Display1.java @@ -0,0 +1,203 @@ +/* + +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); + } + } +} diff --git a/docs/teaching/demos/spacewar/src/spacewar/Display2.java b/docs/teaching/demos/spacewar/src/spacewar/Display2.java new file mode 100644 index 000000000..d2dbeb40e --- /dev/null +++ b/docs/teaching/demos/spacewar/src/spacewar/Display2.java @@ -0,0 +1,138 @@ +/* + +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); + } + } +} diff --git a/docs/teaching/demos/spacewar/src/spacewar/EnergyPacket.java b/docs/teaching/demos/spacewar/src/spacewar/EnergyPacket.java new file mode 100644 index 000000000..d7bc4f9ec --- /dev/null +++ b/docs/teaching/demos/spacewar/src/spacewar/EnergyPacket.java @@ -0,0 +1,44 @@ +/* + +Copyright (c) Xerox Corporation 1998-2002. All rights reserved. + +Use and copying of this software and preparation of derivative works based +upon this software are permitted. Any distribution of this software or +derivative works must comply with all applicable United States export control +laws. + +This software is made available AS IS, and Xerox Corporation makes no warranty +about the software, its performance or its conformity to any specification. + +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| + + +EnergyPacket.java +Part of the Spacewar system. + +*/ + +package spacewar; + + +class EnergyPacket extends SpaceObject { + + static private final int SIZE = 5; //Can't be changed for now!!! + int getSize() { return SIZE; } + + private double energy; + + double getEnergy() { return energy; } + + EnergyPacket(Game theGame, + double xP, double yP, double xV, double yV, double e) { + super(theGame, xP, yP, xV, yV); + energy = e; + } + + void handleCollision(SpaceObject obj) { + die(); + } +} diff --git a/docs/teaching/demos/spacewar/src/spacewar/EnergyPacketProducer.java b/docs/teaching/demos/spacewar/src/spacewar/EnergyPacketProducer.java new file mode 100644 index 000000000..efd276042 --- /dev/null +++ b/docs/teaching/demos/spacewar/src/spacewar/EnergyPacketProducer.java @@ -0,0 +1,63 @@ +/* + +Copyright (c) Xerox Corporation 1998-2002. All rights reserved. + +Use and copying of this software and preparation of derivative works based +upon this software are permitted. Any distribution of this software or +derivative works must comply with all applicable United States export control +laws. + +This software is made available AS IS, and Xerox Corporation makes no warranty +about the software, its performance or its conformity to any specification. + +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| + + +EnergyPacketProducer.java +Part of the Spacewar system. + + This implementation creates booby-trapped packets 20% of the time. + +*/ + +package spacewar; + + +class EnergyPacketProducer extends Thread { + private final static int MIN = -20; + private final static int MAX = 80; + private final static int EXPECTEDINTERVAL = 15; + + private Game game; + + Game getGame() { return game; } + + EnergyPacketProducer(Game theGame) { + super("EnergyPacketProducer"); + game = theGame; + } + + public void run() { + while(true) { + produceAPacket(); + waitForABit(); + } + } + + void waitForABit() { + try { Thread.sleep((int)(Math.random() * EXPECTEDINTERVAL * 2000)); } + catch (InterruptedException e) {} + } + + void produceAPacket() { + EnergyPacket pkt = + new EnergyPacket(game, + Math.random() * getGame().getWidth(), + Math.random() * getGame().getHeight(), + Math.random() * 2 - 1, + Math.random() * 2 - 1, + Math.random() * (MAX - MIN) + MIN); + } +} diff --git a/docs/teaching/demos/spacewar/src/spacewar/EnsureShipIsAlive.java b/docs/teaching/demos/spacewar/src/spacewar/EnsureShipIsAlive.java new file mode 100644 index 000000000..f7b949a92 --- /dev/null +++ b/docs/teaching/demos/spacewar/src/spacewar/EnsureShipIsAlive.java @@ -0,0 +1,35 @@ +/* + +Copyright (c) Xerox Corporation 1998-2002. All rights reserved. + +Use and copying of this software and preparation of derivative works based +upon this software are permitted. Any distribution of this software or +derivative works must comply with all applicable United States export control +laws. + +This software is made available AS IS, and Xerox Corporation makes no warranty +about the software, its performance or its conformity to any specification. + +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| + +Ship.java +Part of the Spacewar system. + +*/ + +package spacewar; + +/** + * This aspect makes sure that the ship is alive before performing any console + * commands. + * + */ +aspect EnsureShipIsAlive { + void around (Ship ship): Ship.helmCommandsCut(ship) { + if ( ship.isAlive() ) { + proceed(ship); + } + } +} diff --git a/docs/teaching/demos/spacewar/src/spacewar/Game.java b/docs/teaching/demos/spacewar/src/spacewar/Game.java new file mode 100644 index 000000000..da67d7bc3 --- /dev/null +++ b/docs/teaching/demos/spacewar/src/spacewar/Game.java @@ -0,0 +1,215 @@ +/* + +Copyright (c) Xerox Corporation 1998-2002. All rights reserved. + +Use and copying of this software and preparation of derivative works based +upon this software are permitted. Any distribution of this software or +derivative works must comply with all applicable United States export control +laws. + +This software is made available AS IS, and Xerox Corporation makes no warranty +about the software, its performance or its conformity to any specification. + +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| + + +Game.java +Part of the Spacewar system. + +*/ + +package spacewar; + +import java.awt.Dimension; + +/** + * The Game class is the root of the spacewar game. To start a spacewar + * game, you can either call the main method, or instantiate this class + * directly. + * + * Synchronization is done by the GameSynchronization aspect. + */ +public class Game extends Thread { + + /** + * To run the game from top level, simply say Java Game, as usual. Passing + * an argument makes the game run in demo mode. Without an argument it runs + * in the normal player mode. + */ + public static void main(String[] args) { + if ( args.length == 0 ) + new Game("1").run(); + new Game(args[0]).run(); + } + + + private Timer timer; + private EnergyPacketProducer ePP; + + private Registry registry; + private Pilot pilot1, pilot2; + + private Dimension screenSize = new Dimension(500, 500); + + Registry getRegistry() { return registry; } + Pilot getPilot1() { return pilot1; } + Pilot getPilot2() { return pilot2; } + + /** returns the width of the screen, delegating to screenSize */ + int getWidth() { return screenSize.width; } + + /** returns the height of the screen, delegating to screenSize */ + int getHeight() { return screenSize.height; } + + /** + * To run the game, simply instantiate this class. It runs in its own + * thread. You can instantiate multiple games at once. For the time being + * the only way to end the game is to exit from the Java VM. + * + * @param isDemo Controls whether the game runs in demo mode or not. True + * means it is a demo, false means it runs in normal 2 player mode. + */ + public Game(String mode) { + timer = new Timer(this); + ePP = new EnergyPacketProducer(this); + registry = new Registry(this); + } + + public void run() { + timer.start(); + ePP.start(); + + while(true) { + try { + newRobot(3); + Thread.sleep(15000); + } + catch (InterruptedException e) {} + } + } + + + /** + * add a robot to the game. This is a menu command. + */ + void addRobot() { + newRobot(3); + } + + /** + * resurrect the ships in the game. This is a menu command. + */ + void resetShips() { + Ship[] ships = registry.getShips(); + + for (int i = 0; i < ships.length; i++) { + Ship ship = ships[i]; + Pilot pilot = ship.getPilot(); + newShip(pilot); + } + } + + /** + * leave the game. This is a menu command. + */ + void quit() { + System.exit(0); + } + + void error(Object o) { + System.err.println(o); + } + + + /** + * returns a new player. With {@link #newRobot} and {@link + * #newShip}, the only ways to make a Player, a Robot, or a Ship. + * The structural invariant is that there should be no calls to + * new of one of these three classes outside these three methods. + */ + Player newPlayer(int number) { + Player player = new Player(this, number); + newShip(player); + return player; + } + + /** + * returns a new robot. With {@link #newPlayer} and {@link + * #newShip}, the only ways to make a Player, a Robot, or a Ship. + * The structural invariant is that there should be no calls to + * new of one of these three classes outside these three methods. + */ + Robot newRobot(int number) { + Robot robot = new Robot(this, number); + newShip(robot); + robot.start(); + return robot; + } + + /** + * returns a new ship. With {@link #newRobot} and {@link + * #newPlayer}, the only ways to make a Player, a Robot, or a + * Ship. The structural invariant is that there should be no + * calls to new of one of these three classes outside these three + * methods. + */ + Ship newShip(Pilot pilot) { + // + // If there is an old ship (we're doing a reset), then remove it from + // the registry. + // + Ship oldShip = pilot.getShip(); + if (! (oldShip == null)) + oldShip.die(); + + Ship newShip = new Ship(this, + Math.random() * getWidth(), + Math.random() * getHeight(), + Math.random() * Math.PI * 2); + pilot.setShip(newShip); + newShip.setPilot(pilot); + + return newShip; + } + + void clockTick() { + registry.clockTick(); + handleCollisions(); + } + + // collision detection + + void handleCollisions() { + SpaceObject[] objects = registry.getObjects(); + + SpaceObject objI, objJ; + for (int i = 0; i < objects.length; i++) { + objI = objects[i]; + for (int j = i + 1; j < objects.length; j++) { + objJ = objects[j]; + if (objI instanceof Bullet && objJ instanceof Bullet) + continue; + if (isCollision(objI, objJ)) { + if (objI instanceof Ship && objJ instanceof Ship) + Ship.bounce((Ship)(objI), (Ship)(objJ)); + else { + objI.handleCollision(objJ); + objJ.handleCollision(objI); + } + } + } + } + } + + /* + * Is the distance between the two centers less than the sum of the two + * radii. This is a cheap and dirty (i.e. wrong) implementation of this. + */ + static boolean isCollision(SpaceObject a, SpaceObject b) { + return (Math.abs(a.getXPos() - b.getXPos()) + + Math.abs(a.getYPos() - b.getYPos())) < + (a.getSize()/2 + b.getSize()/2); + } +} diff --git a/docs/teaching/demos/spacewar/src/spacewar/GameSynchronization.java b/docs/teaching/demos/spacewar/src/spacewar/GameSynchronization.java new file mode 100644 index 000000000..dcf42e904 --- /dev/null +++ b/docs/teaching/demos/spacewar/src/spacewar/GameSynchronization.java @@ -0,0 +1,54 @@ +/* + +Copyright (c) Xerox Corporation 1998-2002. All rights reserved. + +Use and copying of this software and preparation of derivative works based +upon this software are permitted. Any distribution of this software or +derivative works must comply with all applicable United States export control +laws. + +This software is made available AS IS, and Xerox Corporation makes no warranty +about the software, its performance or its conformity to any specification. + +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| + + +RegistrySynchronization.java +Part of the Spacewar system. + +*/ + +package spacewar; + +import coordination.Coordinator; + +/** + * This aspect ensures synchronized access to methods of the Game in the + * presence of several threads. + * + * It uses the Coordinator class, from the AspectJ coordination library. + * (This case is right on the borderline of being too simple to use the + * coordination library, but we use it anyways to keep the similarity + * with the RegistrySynchronizer.) + * + * It uses a per-Game coordination scheme, so there is one instance of + * this class for each instance of the Game class. When this class is + * constructed, it registers appropriate mutexes and selfexes using + * the behavior inherited from Coordinator. + * + * The coordination constraints for the Game are simple. We just need to + * make sure that newShip and handleCollisions are mutually exclusive. That + * ensures that they we can't destroy a ship that has just been replaced. + */ +aspect GameSynchronization extends Coordinator perthis(this(Game)) { + + protected pointcut synchronizationPoint(): + call(void Game.handleCollisions(..)) || call(Ship Game.newShip(..)); + + public GameSynchronization() { + addMutex(new String[] {"handleCollisions", "newShip"}); + } + +} diff --git a/docs/teaching/demos/spacewar/src/spacewar/Pilot.java b/docs/teaching/demos/spacewar/src/spacewar/Pilot.java new file mode 100644 index 000000000..330d860bf --- /dev/null +++ b/docs/teaching/demos/spacewar/src/spacewar/Pilot.java @@ -0,0 +1,44 @@ +/* + +Copyright (c) Xerox Corporation 1998-2002. All rights reserved. + +Use and copying of this software and preparation of derivative works based +upon this software are permitted. Any distribution of this software or +derivative works must comply with all applicable United States export control +laws. + +This software is made available AS IS, and Xerox Corporation makes no warranty +about the software, its performance or its conformity to any specification. + +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| + + +*/ + +package spacewar; + + +/** + * Pilot is the abstract superclass of Player and Robot. + * + */ + +abstract class Pilot { + private Game game; + private int number; + protected Ship ship = null; + + Game getGame() { return game; } + int getNumber() { return number; } + Ship getShip() { return ship; } + + void setShip(Ship s) { ship = s; } + + Pilot (Game g, int n) { + super(); + game = g; + number = n; + } +} diff --git a/docs/teaching/demos/spacewar/src/spacewar/Player.java b/docs/teaching/demos/spacewar/src/spacewar/Player.java new file mode 100644 index 000000000..ebde6d5c4 --- /dev/null +++ b/docs/teaching/demos/spacewar/src/spacewar/Player.java @@ -0,0 +1,122 @@ +/* + +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; + } +} diff --git a/docs/teaching/demos/spacewar/src/spacewar/Registry.java b/docs/teaching/demos/spacewar/src/spacewar/Registry.java new file mode 100644 index 000000000..a9cec0418 --- /dev/null +++ b/docs/teaching/demos/spacewar/src/spacewar/Registry.java @@ -0,0 +1,126 @@ +/* + +Copyright (c) Xerox Corporation 1998-2002. All rights reserved. + +Use and copying of this software and preparation of derivative works based +upon this software are permitted. Any distribution of this software or +derivative works must comply with all applicable United States export control +laws. + +This software is made available AS IS, and Xerox Corporation makes no warranty +about the software, its performance or its conformity to any specification. + +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| + + +Registry.java +Part of the Spacewar system. + +*/ + +package spacewar; + +import java.util.Vector; +import java.util.Hashtable; +import java.util.Enumeration; + +/** + * The Registry keeps track of all the space objects that are floating around. + * It basically supports register, unregister and contents type operations. + * + * The synchronization is done by the RegistrySynchronization aspect. + */ + +class Registry { + + private Hashtable table; + private Game game; + + Game getGame() { return game; } + + Registry (Game theGame) { + game = theGame; + table = new Hashtable(); + } + + + void register(SpaceObject object) { + table.put(object, object); + } + + void unregister(SpaceObject object) { + table.remove(object); + } + + /* + * It is an invariant of the design that only two points in SpaceObject + * should call register and unregister. This aspect enforces that. + * + * Unfortunately, in the current compiler, we get a static warning when + * there are no illegal calls that this advice has no targets. That will + * be fixed in a future release. For the time being the dummy method + * just below this fixes that. + */ + static aspect RegistrationProtection { + after() returning(): + (call(void Registry.register(SpaceObject)) || + call(void Registry.unregister(SpaceObject))) && + !(within(SpaceObject) && (withincode(new(..)) || + withincode(void die()))) { + throw new IllegalAccessError( + "This is an illegal call to " + thisJoinPoint + "\n" + + "Only the constructor and the die() on SpaceObject\n" + + "should call the primitive registry operations."); + } + } + + void dummy() { // see comment above + register(getObjects()[0]); + unregister(getObjects()[0]); + } + + + SpaceObject[] getObjects() { + SpaceObject[] allObjects = new SpaceObject[table.size()]; + Enumeration elements = table.elements(); + for(int i = 0; elements.hasMoreElements(); i++) { + allObjects[i] = (SpaceObject)(elements.nextElement()); + } + return allObjects; + } + + Ship[] getShips() { + // + // First we have to put just the Ships into a vector, then we can put + // them into an array of exactly the right length. + // + Ship[] arrayOfShips; + Vector vectorOfShips = new Vector(); + Enumeration elements = table.elements(); + while (elements.hasMoreElements()) { + Object object = elements.nextElement(); + if (object instanceof Ship) { + vectorOfShips.addElement(object); + } + } + + arrayOfShips = new Ship[(vectorOfShips.size())]; + vectorOfShips.copyInto(arrayOfShips); + return arrayOfShips; + } + + Hashtable getTable() { return table; } + + // + // The protocol for clockTick is that it automatically cascades. + // + void clockTick() { + Enumeration elements = table.elements(); + while (elements.hasMoreElements()) { + ((SpaceObject)elements.nextElement()).clockTick(); + } + } +} + diff --git a/docs/teaching/demos/spacewar/src/spacewar/RegistrySynchronization.java b/docs/teaching/demos/spacewar/src/spacewar/RegistrySynchronization.java new file mode 100644 index 000000000..986e4bd30 --- /dev/null +++ b/docs/teaching/demos/spacewar/src/spacewar/RegistrySynchronization.java @@ -0,0 +1,58 @@ +/* + +Copyright (c) Xerox Corporation 1998-2002. All rights reserved. + +Use and copying of this software and preparation of derivative works based +upon this software are permitted. Any distribution of this software or +derivative works must comply with all applicable United States export control +laws. + +This software is made available AS IS, and Xerox Corporation makes no warranty +about the software, its performance or its conformity to any specification. + +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| + + +RegistrySynchronization.java +Part of the Spacewar system. + +*/ + +package spacewar; + +import coordination.Coordinator; + + +/** + * This aspect ensures synchronized access to methods of the Registry in + * the presence of several threads. + * + * It uses the Coordinator class, from the AspectJ coordination library. + * + * It uses a per-Registry coordination scheme, so there is one instance of + * this class for each instance of the Registry class. When this class is + * constructed, it registers appropriate mutexes and selfexes using the + * behavior inherited from Coordinator. + * + * The mutating methods (register and unregister) should be self-exclusive. + * Each reader method should be mutually exclusive with the mutating + * methods. But the readers can run concurrently. */ +aspect RegistrySynchronization extends Coordinator perthis(this(Registry)) { + + protected pointcut synchronizationPoint(): + call(void Registry.register(..)) || + call(void Registry.unregister(..)) || + call(SpaceObject[] Registry.getObjects(..)) || + call(Ship[] Registry.getShips(..)); + + public RegistrySynchronization() { + addSelfex("register"); + addSelfex("unregister"); + + addMutex(new String[] {"register", "unregister", "getObjects"}); + addMutex(new String[] {"register", "unregister", "getShips"}); + } + +} diff --git a/docs/teaching/demos/spacewar/src/spacewar/Robot.java b/docs/teaching/demos/spacewar/src/spacewar/Robot.java new file mode 100644 index 000000000..05f276108 --- /dev/null +++ b/docs/teaching/demos/spacewar/src/spacewar/Robot.java @@ -0,0 +1,201 @@ +/* + +Copyright (c) Xerox Corporation 1998-2002. All rights reserved. + +Use and copying of this software and preparation of derivative works based +upon this software are permitted. Any distribution of this software or +derivative works must comply with all applicable United States export control +laws. + +This software is made available AS IS, and Xerox Corporation makes no warranty +about the software, its performance or its conformity to any specification. + +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| + + +Robot.java +Part of the Spacewar system. + +*/ + +package spacewar; + +import java.util.Random; + +/** + * Robot is an automatic pilot that now has quite a bit of intelligence. + * So, beware ! + */ +class Robot extends Pilot implements Runnable { + + private static final int FIRE_INTERVAL = 60; + private static final int REBIRTH_DELAY = 900; + + private final Random random = new Random(); + + private Thread runner; + private boolean runnable = true; + + Robot(Game theGame, int number) { + super(theGame, number); + } + + void start() { + if (runner == null) { + runner = new Thread(this); + runner.start(); + } + } + + void destroy() { + if (runner != null) { + runnable = false; + runner = null; + } + } + + + // A Robot tracks User-controlled ships and fires at them + public void run() { + Ship target = null; + + while(runnable) { + // find target ship + do { + Ship[] potentials = getGame().getRegistry().getShips(); + if(potentials.length != 0) + target = potentials[Math.abs(random.nextInt() % potentials.length)]; + sleepForABit(25); + } while (target == ship); + // main loop + int currentRotation = Ship.STOP; + int time; + boolean currentlyAccelerating = false; + double dx, dy, angleA, angleB, theta, dtheta, d, + targetVel, a, b, c, targetXVel, targetYVel; + + while(true) { + sleepForABit(FIRE_INTERVAL); + + // if my ship is destroyed, give me a new one + if (!ship.isAlive()) { + sleepForABit(REBIRTH_DELAY); + getGame().newShip(this); + } + + // find direction and distance from target to me + dx = ship.getXPos() - target.getXPos(); + if (dx < - getGame().getWidth() / 2) + dx += getGame().getWidth(); + if (dx > getGame().getWidth() / 2) + dx -= getGame().getWidth(); + dy = ship.getYPos() - target.getYPos(); + if (dy < - getGame().getHeight() / 2) + dy += getGame().getHeight(); + if (dy > getGame().getHeight() / 2) + dy -= getGame().getHeight(); + d = Math.sqrt(dx * dx + dy * dy); + angleA = Math.atan(dy / dx); + if (dx < 0) + angleA += Math.PI; + + // find relative velocity and trajectory of target + targetXVel = target.getXVel() - ship.getXVel(); + targetYVel = target.getYVel() - ship.getYVel(); + targetVel = Math.sqrt(targetXVel * targetXVel + + targetYVel * targetYVel); + angleB = Math.atan(targetYVel / targetXVel); + if (targetXVel < 0) + angleB+=Math.PI; + + // find angle between line to target and taget's direction of travel + theta = (angleA - angleB) % (2 * Math.PI); + if (theta < -Math.PI) + theta += 2 * Math.PI; + if (theta > Math.PI) + theta -= 2 * Math.PI; + + // calculate time to bullet impact using law of cosines + a = targetVel * targetVel + Ship.BULLET_SPEED * Ship.BULLET_SPEED; + b = d * targetVel * Math.cos(theta); + c = - d * d; + time = (int)((-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a); + + // calculate angle and distance to bullet impact location + dx = targetXVel * time - dx; + dy = targetYVel * time - dy; + theta = Math.atan(dy / dx); + if(dx < 0) + theta += Math.PI; + + // find desired change in rotation + dtheta = (theta - ship.getOrientation()) % (2 * Math.PI); + // find the shortest path to the desired orientation; + if(dtheta < - Math.PI) + dtheta += 2 * Math.PI; + if(dtheta > Math.PI) + dtheta -= 2 * Math.PI; + + // turn if nessecary + if (dtheta > Ship.DEFAULT_ANGULAR_VELOCITY / 2) { + if (currentRotation != Ship.CLOCKWISE) + ship.rotate(currentRotation = Ship.CLOCKWISE); + } + else if (dtheta < -Ship.DEFAULT_ANGULAR_VELOCITY / 2) { + if (currentRotation != Ship.COUNTERCLOCKWISE) + ship.rotate(currentRotation = Ship.COUNTERCLOCKWISE); + } // otherwise, fire, maybe even a burst + else { + if(currentRotation != Ship.STOP) + ship.rotate(currentRotation = Ship.STOP); + if (random.nextInt() % 40 == 0) { + ship.fire(); + } + } + + // randomly accelerate + if (currentlyAccelerating && random.nextInt() % 2 == 0) + ship.thrust(currentlyAccelerating = false); + else { + if (ship.getXVel() == 0) + angleA = 0; + else + angleA = Math.atan(ship.getYVel() / ship.getXVel()); + + if (ship.getXVel() < 0) + angleA+=Math.PI; + angleB = (angleA - ship.getOrientation()) % (2 * Math.PI); + if (angleB < -Math.PI) + angleB += 2 * Math.PI; + if (angleB > Math.PI) + angleB -= 2 * Math.PI; + angleB = Math.abs(angleB); + + // angleB now represents the angle between the ship's + // orientation and velocity vector. This will be used to + // determine the probably that the ship will thrust to + // prevent ships from accelerating too much in one direction + if (random.nextInt() % (int)(12 * (Math.PI - angleB) + 1) == 0) + ship.thrust(currentlyAccelerating = true); + } + + // switch targets if current one has been destroyed + if (target.getDamage() == 100) + break; + + // randomly switch targets + if (random.nextInt() % 4000 == 0) + break; + } + } + } + + void sleepForABit (int time) { + try { + runner.sleep(time); + } + catch (InterruptedException e) {} + } +} diff --git a/docs/teaching/demos/spacewar/src/spacewar/SWFrame.java b/docs/teaching/demos/spacewar/src/spacewar/SWFrame.java new file mode 100644 index 000000000..6dfb9f6f6 --- /dev/null +++ b/docs/teaching/demos/spacewar/src/spacewar/SWFrame.java @@ -0,0 +1,92 @@ +/* + +Copyright (c) Xerox Corporation 1998-2002. All rights reserved. + +Use and copying of this software and preparation of derivative works based +upon this software are permitted. Any distribution of this software or +derivative works must comply with all applicable United States export control +laws. + +This software is made available AS IS, and Xerox Corporation makes no warranty +about the software, its performance or its conformity to any specification. + +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| + + +SWFrame.java +Part of the Spacewar system. + +*/ + +package spacewar; + +import java.awt.Frame; +import java.awt.Menu; +import java.awt.MenuBar; +import java.awt.MenuItem; +import java.awt.MenuShortcut; +import java.awt.Dimension; +import java.awt.Insets; + +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; + +class SWFrame extends Frame implements ActionListener { + private Game game; + private Display display; + private Menu menu; + + Game getGame() { return game; } + Display getDisplay() { return display; } + Menu getMenu() { return menu; } + + SWFrame(Game theGame, Display d) { + super("Space War!"); + + game = theGame; + + display = d; + add(display); + + // create menu + menu = new Menu("Game"); + MenuItem item1 = new MenuItem("Add Robot", new MenuShortcut('a')); + MenuItem item2 = new MenuItem("Reset Ships", new MenuShortcut('r')); + MenuItem item3 = new MenuItem("Quit", new MenuShortcut('q')); + item1.setActionCommand("Add Robot"); + item2.setActionCommand("Reset Ships"); + item3.setActionCommand("Quit"); + menu.add(item1); + menu.add(item2); + menu.add(item3); + menu.addActionListener(this); + + setMenuBar(new MenuBar()); + getMenuBar().add(menu); + + Dimension screenSize = new Dimension(500, 500); + setSize(screenSize); + setVisible(true); + toFront(); + + Insets inset = getInsets(); + int displayWidth = screenSize.width - inset.left - inset.right; + int displayHeight = screenSize.height - inset.top - inset.bottom; + display.setSize(displayWidth, displayHeight); + } + + public void actionPerformed(ActionEvent e) { + String s = e.getActionCommand(); + if (s.equals("Add Robot")) { + getGame().addRobot(); + } + else if (s.equals("Reset Ships")) { + getGame().resetShips(); + } + else if (s.equals("Quit")) { + getGame().quit(); + } + } +} diff --git a/docs/teaching/demos/spacewar/src/spacewar/Ship.java b/docs/teaching/demos/spacewar/src/spacewar/Ship.java new file mode 100644 index 000000000..8f016fe27 --- /dev/null +++ b/docs/teaching/demos/spacewar/src/spacewar/Ship.java @@ -0,0 +1,298 @@ +/* + +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; + +} diff --git a/docs/teaching/demos/spacewar/src/spacewar/SpaceObject.java b/docs/teaching/demos/spacewar/src/spacewar/SpaceObject.java new file mode 100644 index 000000000..ee3afabcc --- /dev/null +++ b/docs/teaching/demos/spacewar/src/spacewar/SpaceObject.java @@ -0,0 +1,106 @@ +/* + +Copyright (c) Xerox Corporation 1998-2002. All rights reserved. + +Use and copying of this software and preparation of derivative works based +upon this software are permitted. Any distribution of this software or +derivative works must comply with all applicable United States export control +laws. + +This software is made available AS IS, and Xerox Corporation makes no warranty +about the software, its performance or its conformity to any specification. + +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| + + +SpaceObject.java +Part of the Spacewar system. + +*/ + +package spacewar; + + +/** + * SpaceObjects are objects that float around in space. They support the + * minimal SpaceObject protocol, having to do with position, velocity, + * size and liveness. They are constructed with game, position, velocity + * and size. When constructed, a spaceobject adds itself to the registry. + * + * When it dies, a spaceobject removes itself from the registry. But note + * that it doesn't decide when to die, subclasses do that. + * + * The display aspects actually draw the space object on the screen and say + * how much space it takes up there. + */ +abstract class SpaceObject { + + private Game game; + private double xPos, yPos, oldXPos, oldYPos, xVel, yVel; + private boolean alive; + + SpaceObject (Game theGame, double xP, double yP, double xV, double yV) { + game = theGame; + xPos = xP; + yPos = yP; + oldXPos = xP; + oldYPos = yP; + xVel = xV; + yVel = yV; + + alive = true; + getGame().getRegistry().register(this); + } + + Game getGame() { return game; } + + double getXPos() { return xPos; } + double getYPos() { return yPos; } + + double getOldXPos() { return oldXPos; } + double getOldYPos() { return oldYPos; } + + double getXVel() { return xVel; } + double getYVel() { return yVel; } + + void setXVel (double n) { xVel = n; } + void setYVel (double n) { yVel = n; } + + boolean isAlive() { return alive; } + void setIsAlive(boolean n) { alive = n; } + + + /** + * Move 1 unit of time's worth of distance. I.e. increment xPos by xVel + * and yPos by yVel. If we move off an edge of the screen move us back + * in the opposite edge. + */ + void clockTick() { + oldXPos = xPos; + oldYPos = yPos; + xPos = (xPos + xVel) % getGame().getWidth(); + if(xPos < 0) + xPos += getGame().getWidth(); + yPos = (yPos + yVel) % getGame().getHeight(); + if(yPos < 0) + yPos += getGame().getHeight(); + } + + void accelerate(double dXVel, double dYVel) { + xVel += dXVel; + yVel += dYVel; + } + + void die() { + getGame().getRegistry().unregister(this); + } + + abstract int getSize(); + + /** resolve the effects of colliding with a space object. + * @param obj the space object that this object is colliding with. + */ + abstract void handleCollision(SpaceObject obj); +} diff --git a/docs/teaching/demos/spacewar/src/spacewar/Timer.java b/docs/teaching/demos/spacewar/src/spacewar/Timer.java new file mode 100644 index 000000000..1f4a992a7 --- /dev/null +++ b/docs/teaching/demos/spacewar/src/spacewar/Timer.java @@ -0,0 +1,53 @@ +/* + +Copyright (c) Xerox Corporation 1998-2002. All rights reserved. + +Use and copying of this software and preparation of derivative works based +upon this software are permitted. Any distribution of this software or +derivative works must comply with all applicable United States export control +laws. + +This software is made available AS IS, and Xerox Corporation makes no warranty +about the software, its performance or its conformity to any specification. + +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| +|<--- this code is formatted to fit into 80 columns --->| + + +Timer.java +Part of the Spacewar system. + +*/ + +package spacewar; + + +class Timer extends Thread { + + private final static int TICK_PERIOD = 40; // time between ticks in millis + + private Game game; + + Game getGame() { return game; } + + Timer (Game theGame) { + super("Timer"); + game = theGame; + } + + public void run() { + long t1, tdiff; + while (true) { + t1 = System.currentTimeMillis(); + getGame().clockTick(); + tdiff = System.currentTimeMillis() - t1; + if (tdiff < TICK_PERIOD) { + try { + sleep (Math.max(0 , TICK_PERIOD - tdiff)); + } + catch (InterruptedException e) { } + } + } + } +} |