--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package answers;
+
+import figures.*;
+
+aspect Answer1a {
+ declare error
+ : get(java.io.PrintStream System.out)
+ && within(figures..*)
+ : "illegal access to System.out";
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package answers;
+
+aspect Answer1b {
+ declare warning
+ : set(private * *)
+ && !withincode(* set*(..))
+ && within(figures.*)
+ : "bad field set";
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package answers;
+
+aspect Answer1c {
+ declare error
+ : set(private * *)
+ && !(withincode(* set*(..)) || withincode(new(..)))
+ && within(figures.*)
+ : "bad field set";
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package answers;
+
+aspect Answer1c {
+ declare error
+ : set(private * *)
+ && !(withincode(* set*(..))
+ || withincode(new(..))
+ || withincode(void figures.Point.move(int, int)))
+ && within(figures.*)
+ : "bad field set";
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package answers;
+
+import figures.Point;
+import figures.FigureElement;
+
+public aspect Answer2a {
+ before(int newValue): set(int Point.*) && args(newValue) {
+ if (newValue < FigureElement.MIN_VALUE) {
+ throw new IllegalArgumentException("too small");
+ } else if (newValue > FigureElement.MAX_VALUE) {
+ throw new IllegalArgumentException("too large");
+ }
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package answers;
+
+import figures.Group;
+import figures.FigureElement;
+
+public aspect Answer2b {
+ before(FigureElement newValue):
+ call(void Group.add(FigureElement))
+ && args(newValue) {
+ if (newValue == null) {
+ throw new IllegalArgumentException("null not allowed");
+ }
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package answers;
+
+import figures.Group;
+import figures.FigureElement;
+
+public aspect Answer2c {
+ before(FigureElement newValue, Group g):
+ call(void Group.add(FigureElement))
+ && args(newValue)
+ && target(g) {
+ if (newValue == null) {
+ throw new IllegalArgumentException("null not allowed");
+ }
+ if (newValue == g) {
+ throw new IllegalArgumentException("self not allowed");
+ }
+
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package answers;
+
+import figures.SlothfulPoint;
+
+aspect Answer2d {
+ after(int newValue, SlothfulPoint p) returning:
+ call(void setX(int)) && args(newValue) && target(p) {
+ if (newValue != p.getX()) {
+ throw new RuntimeException("setter didn't set");
+ }
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package answers;
+
+import figures.*;
+
+public aspect Answer2e {
+ pointcut checkpoint(Box box):
+ call(void move(int, int)) && target(box);
+
+ after(Box box) returning: checkpoint(box) {
+ box.checkBoxness();
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package answers;
+
+import figures.*;
+
+public aspect Answer2f {
+ pointcut checkpoint(Box box):
+ target(box) && call(public * *(..)) && !within(Answer*);
+
+ after(Box box) returning: checkpoint(box) {
+ box.checkBoxness();
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package answers;
+
+import figures.*;
+
+aspect Answer2g {
+ int around(int val): (set(int Point._x) || set(int Point._y))
+ && args(val) {
+ return proceed(trim(val));
+ }
+
+ private int trim(int val) {
+ return Math.max(Math.min(val, FigureElement.MAX_VALUE),
+ FigureElement.MIN_VALUE);
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package answers;
+
+import figures.*;
+
+import java.awt.Rectangle;
+
+aspect Answer2h {
+ void around(FigureElement fe, int dx, int dy):
+ target(fe) && call(void move(int, int)) && args(dx, dy) {
+ Rectangle preBounds = new Rectangle(fe.getBounds());
+ proceed(fe, dx, dy);
+
+ preBounds.translate(dx, dy);
+
+ if (!preBounds.equals(fe.getBounds())) {
+ throw new IllegalStateException("bounds don't match move");
+ }
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package answers;
+
+import support.Log;
+
+import figures.Point;
+
+aspect Answer3a {
+ before(): execution(void Point.move(int, int)) {
+ Log.log("moving");
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package answers;
+
+import support.Log;
+
+import figures.Point;
+import figures.Group;
+import figures.FigureElement;
+
+aspect Answer3b {
+ before():
+ execution(void Group.add(FigureElement)) && args(Point) {
+ Log.log("adding Point");
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package answers;
+
+import support.Log;
+
+import figures.Point;
+import figures.Group;
+import figures.FigureElement;
+
+aspect Answer3c {
+ private Group Point.enclosingGroup = null;
+
+ before(Point p, Group g):
+ execution(void add(FigureElement)) && args(p) && target(g) {
+ p.enclosingGroup = g;
+ }
+
+ before(Point p):
+ call(void move(int, int)) && target(p) {
+ Log.log("moving as a part of " + p.enclosingGroup);
+ }
+
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package answers;
+
+import figures.FigureElement;
+import figures.Group;
+import java.awt.Rectangle;
+
+aspect Answer4a {
+ private Rectangle wholeCanvas =
+ new Rectangle(FigureElement.MIN_VALUE, FigureElement.MIN_VALUE,
+ FigureElement.MAX_VALUE - FigureElement.MIN_VALUE,
+ FigureElement.MAX_VALUE - FigureElement.MIN_VALUE);
+
+ Rectangle around(): execution(Rectangle Group.getBounds()) {
+ return wholeCanvas;
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package answers;
+
+import figures.FigureElement;
+import figures.Group;
+import java.awt.Rectangle;
+
+aspect Answer4b {
+ private Rectangle Group.cache = null;
+
+ Rectangle around(Group g):
+ execution(Rectangle Group.getBounds()) && this(g) {
+ if (g.cache == null) {
+ g.cache = proceed(g);
+ }
+ return g.cache;
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package answers;
+
+import figures.FigureElement;
+import figures.Group;
+import java.awt.Rectangle;
+
+aspect Answer4c {
+ private Rectangle Group.cache = null;
+
+ Rectangle around(Group g):
+ execution(Rectangle Group.getBounds()) && this(g) {
+ if (g.cache == null) {
+ g.cache = proceed(g);
+ }
+ return g.cache;
+ }
+
+ before(Group g): call(void move(int, int)) && target(g) {
+ g.cache = null;
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package answers;
+
+import figures.FigureElement;
+import figures.Group;
+import figures.Point;
+import java.awt.Rectangle;
+
+aspect Answer4d {
+ private Rectangle Group.cache = null;
+ private Group Point.enclosingGroup = null;
+
+ before(Point p, Group g):
+ execution(void add(FigureElement)) && args(p) && target(g) {
+ p.enclosingGroup = g;
+ }
+
+ Rectangle around(Group g):
+ execution(Rectangle Group.getBounds()) && this(g) {
+ if (g.cache == null) {
+ g.cache = proceed(g);
+ }
+ return g.cache;
+ }
+
+ before(Point p): set(* Point.*) && target(p) {
+ if (p.enclosingGroup != null) {
+ p.enclosingGroup.cache = null;
+ }
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package answers;
+
+import figures.FigureElement;
+import figures.Group;
+import figures.Point;
+import java.awt.Rectangle;
+
+aspect Answer4e {
+ private Rectangle Group.cache = null;
+ private Group FigureElement.enclosingGroup = null;
+
+ before(FigureElement p, Group g):
+ execution(void add(FigureElement)) && args(p) && target(g) {
+ p.enclosingGroup = g;
+ }
+
+ Rectangle around(Group g):
+ execution(Rectangle Group.getBounds()) && this(g) {
+ if (g.cache == null) {
+ g.cache = proceed(g);
+ }
+ return g.cache;
+ }
+
+ before(Point p): set(* Point.*) && target(p) {
+ FigureElement fe = p;
+ while (fe.enclosingGroup != null) {
+ fe.enclosingGroup.cache = null;
+ fe = fe.enclosingGroup;
+ }
+ }
+}
--- /dev/null
+figures/Box.java
+figures/FigureElement.java
+figures/Group.java
+figures/Line.java
+figures/Point.java
+figures/ShapeFigureElement.java
+figures/SlothfulPoint.java
+
+support/Log.java
+
+answers/Answer.java
+
+tests/Test.java
+tests/Test2a.java
+tests/Test2b.java
+tests/Test2c.java
+tests/Test2d.java
+tests/Test2e.java
+tests/Test2f.java
+tests/Test2g.java
+tests/Test2h.java
+tests/Test3a.java
+tests/Test3b.java
+tests/Test3c.java
+tests/Test4a.java
+tests/Test4b.java
+tests/Test4c.java
+tests/Test4d.java
+tests/Test4e.java
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+
+package figures;
+
+import java.awt.*;
+import java.awt.geom.*;
+
+public class Box extends ShapeFigureElement {
+ private Point _p0;
+ private Point _p1;
+ private Point _p2;
+ private Point _p3;
+
+ public Box(int x0, int y0, int width, int height) {
+ _p0 = new Point(x0, y0);
+ _p1 = new Point(x0+width, y0);
+ _p2 = new Point(x0+width, y0+height);
+ _p3 = new Point(x0, y0+height);
+ }
+
+ public Point getP0() { return _p0; }
+ public Point getP1() { return _p1; }
+ public Point getP2() { return _p2; }
+ public Point getP3() { return _p3; }
+
+ public void move(int dx, int dy) {
+ _p0.move(dx, dy);
+ _p1.move(dx, dy);
+ _p2.move(dx, dy);
+ _p3.move(dx, dy);
+ }
+
+ public void checkBoxness() {
+ if ((_p0.getX() == _p3.getX()) &&
+ (_p1.getX() == _p2.getX()) &&
+ (_p0.getY() == _p1.getY()) &&
+ (_p2.getY() == _p3.getY()))
+ return;
+ throw new IllegalStateException("This is not a square.");
+ }
+
+ public String toString() {
+ return "Box(" + _p0 + ", " + _p1 + ", " + _p2 + ", " + _p3 + ")";
+ }
+
+ public Shape getShape() {
+ return new Rectangle(getP1().getX(),
+ getP1().getY(),
+ getP3().getX() - getP1().getX(),
+ getP3().getY() - getP1().getY());
+ }
+}
+
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+
+package figures;
+
+import java.awt.*;
+import java.awt.geom.*;
+
+public interface FigureElement {
+ public static final int MIN_VALUE = 0;
+ public static final int MAX_VALUE = 500;
+
+ public abstract void move(int dx, int dy);
+
+ public abstract Rectangle getBounds();
+
+ public abstract boolean contains(Point2D p);
+
+ public abstract void paint(Graphics2D g2);
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+
+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 setIdentifier(String identifier) {
+ _identifier = identifier;
+ }
+
+ public String toString() {
+ if (_identifier != null) {
+ return _identifier;
+ }
+
+ StringBuffer buf = new StringBuffer("Group(");
+ for (Iterator i = _members.iterator(); i.hasNext(); ) {
+ buf.append(i.next().toString());
+ if (i.hasNext()) {
+ buf.append(", ");
+ }
+ }
+ buf.append(")");
+ return buf.toString();
+ }
+
+ public Rectangle getBounds() {
+ Rectangle previous = null;
+ for (Iterator i = _members.iterator(); i.hasNext(); ) {
+ FigureElement fe = (FigureElement)i.next();
+ Rectangle rect = fe.getBounds();
+ if (previous != null) {
+ previous = previous.union(rect);
+ } else {
+ previous = rect;
+ }
+ }
+ return previous;
+ }
+
+ public boolean contains(Point2D p) {
+ for (Iterator i = _members.iterator(); i.hasNext(); ) {
+ FigureElement fe = (FigureElement)i.next();
+ if (fe.contains(p)) return true;
+ }
+ return false;
+ }
+
+ public void paint(Graphics2D g2) {
+ for (Iterator i = _members.iterator(); i.hasNext(); ) {
+ FigureElement fe = (FigureElement)i.next();
+ fe.paint(g2);
+ }
+ }
+
+ public int size() {
+ return _members.size();
+ }
+}
+
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+
+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 Point getP2() { return _p2; }
+
+ public void move(int dx, int dy) {
+ _p1.move(dx, dy);
+ _p2.move(dx, dy);
+ }
+
+ public String toString() {
+ return "Line(" + _p1 + ", " + _p2 + ")";
+ }
+
+ /**
+ * Used to determine if this line {@link contains(Point2D)} a point.
+ */
+ final static int THRESHHOLD = 5;
+
+ /**
+ * Returns <code>true</code> if the point segment distance is less than
+ * {@link THRESHHOLD}.
+ */
+ public boolean contains(Point2D p) {
+ return getLine2D().ptLineDist(p) < THRESHHOLD;
+ }
+
+ private Line2D getLine2D() {
+ return new Line2D.Float((float)getP1().getX(),
+ (float)getP1().getY(),
+ (float)getP2().getX(),
+ (float)getP2().getY());
+ }
+
+ public Shape getShape() {
+ return getLine2D();
+ }
+}
+
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+
+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 int getY() { return _y; }
+
+ public void setX(int x) { _x = x; }
+
+ public void setY(int y) { _y = y; }
+
+ public void move(int dx, int dy) {
+ _x += dx;
+ _y += dy;
+ }
+
+ public String toString() {
+ return "Point(" + _x + ", " + _y + ")";
+ }
+
+ /** The height of displayed {@link Point}s. */
+ private final static int HEIGHT = 10;
+
+ /** The width of displayed {@link Point}s. -- same as {@link HEIGHT}. */
+ private final static int WIDTH = Point.HEIGHT;
+
+ public Shape getShape() {
+ return new Ellipse2D.Float((float)getX()-Point.WIDTH/2,
+ (float)getY()-Point.HEIGHT/2,
+ (float)Point.HEIGHT,
+ (float)Point.WIDTH);
+ }
+}
+
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+
+package figures;
+
+import java.awt.*;
+import java.awt.geom.*;
+
+public abstract class ShapeFigureElement implements FigureElement {
+ public abstract void move(int dx, int dy);
+
+ public abstract Shape getShape();
+
+ public Rectangle getBounds() {
+ return getShape().getBounds();
+ }
+
+ public boolean contains(Point2D p) {
+ return getShape().contains(p);
+ }
+
+ public Color getLineColor() {
+ return Color.black;
+ }
+
+ public Color getFillColor() {
+ return Color.red;
+ }
+
+ public final void paint(Graphics2D g2) {
+ Shape shape = getShape();
+ g2.setPaint(getFillColor());
+ g2.fill(shape);
+ g2.setPaint(getLineColor());
+ g2.draw(shape);
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+
+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 int getX() { return _x; }
+
+ public int getY() { return _y; }
+
+ public void setX(int x) { }
+
+ public void setY(int y) { }
+
+ public void move(int dx, int dy) {
+ System.out.println("Slothful moving");
+ }
+
+ public String toString() {
+ return "SlothfulPoint";
+ }
+
+ public Shape getShape() {
+ return new Ellipse2D.Float((float)_x,
+ (float)_y, 1.0f, 1.0f);
+ }
+}
+
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+
+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 {
+ 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 {
+ Group canvas;
+
+ public FigureSurface() {
+ canvas = new Group(new Point(250, 250));
+ addMouseMotionListener(this);
+ addMouseListener(this);
+ setPreferredSize(new Dimension(500,500));
+ }
+
+ private Point addPoint(int x, int y) {
+ Point p = new Point(x, y);
+ canvas.add(p);
+ repaint();
+ return p;
+ }
+
+ private Line addLine(Point p1, Point p2) {
+ if (Math.abs(p1.getX()-p2.getX()) < 5 ||
+ Math.abs(p1.getY()-p2.getY()) < 5) {
+ return null;
+ }
+
+ Line line = null;
+ if (p1 != null && p2 != null) {
+ line = new Line(p1, p2);
+ canvas.add(line);
+ }
+ repaint();
+ return line;
+ }
+
+ public void paint(Graphics g) {
+ Graphics2D g2 = (Graphics2D) g;
+ g2.setPaint(BACKGROUND);
+ g2.fill(new Rectangle2D.Float(0f, 0f, (float)g2.getClipBounds().width, (float)g2.getClipBounds().height));
+ g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ canvas.paint(g2);
+ }
+
+
+ int lastX, lastY;
+ int pressX, pressY;
+
+ FigureElement first = null;
+ Point point1 = null;
+
+ public void mousePressed(MouseEvent e){
+ int x = e.getX(), y = e.getY();
+ pressX = lastX = x; pressY = lastY = y;
+ first = findFigureElement(x, y);
+ if (first == null) {
+ point1 = addPoint(x, y);
+ }
+ }
+
+ public void mouseDragged(MouseEvent e) {
+ int x = e.getX(), y = e.getY(), dx = lastX-x, dy = lastY-y;
+ lastX = x;
+ lastY = y;
+ if (first == null) {
+ Line line = addLine(point1, new Point(x, y));
+ if (line != null) {
+ canvas.add(line.getP2());
+ first = line.getP2();
+ canvas.add(line);
+ }
+ } else {
+ first.move(-dx, -dy);
+ }
+ repaint();
+ }
+
+ public void mouseReleased(MouseEvent e){
+ mouseDragged(e);
+ first = null;
+ point1 = null;
+ }
+
+
+ public void mouseMoved(MouseEvent e){}
+ public void mouseClicked(MouseEvent e){}
+ public void mouseExited(MouseEvent e){}
+ public void mouseEntered(MouseEvent e){}
+
+ private FigureElement findFigureElement(int x, int y) {
+ Point2D p = new Point2D.Float((float)x, (float)y);
+ for (Iterator i = canvas.members(); i.hasNext(); ) {
+ FigureElement fe = (FigureElement)i.next();
+ if (fe.contains(p)) return fe;
+ }
+ return null;
+ }
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+
+package figures.gui;
+
+import support.Log;
+
+aspect LogAdapter {
+
+ before(String s): call(void Log.log(String)) && args(s) {
+ if (Main.panel != null) {
+ Main.panel.cp.println(s);
+ }
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+
+package figures.gui;
+
+import javax.swing.*;
+import support.Log;
+
+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);
+ }
+
+}
--- /dev/null
+-argfile
+base.lst
+figures/gui/FigurePanel.java
+figures/gui/Main.java
+figures/gui/LogAdapter.java
\ No newline at end of file
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html> <head>
+<title>AspectJ Tutorial Exercises</title>
+</head>
+
+<body bgcolor="white">
+<h1>AspectJ Tutorial Exercises</h1>
+
+<h3>Organization</h3>
+
+<p> The exercises work with a figure editor together with JUnit test
+cases. They progress, as most users do in their adoption of AspectJ,
+from non-functional, development-only aspects to aspects which augment
+a deployed program with crosscutting features. </p>
+
+<p> We have made available a package that includes the tests, the base
+code, JUnit, and a distribution of AspectJ. All it needs is
+information about where Java lives (so set your JAVA_HOME environment
+variable). It assumes that you unzip it in c:\ (on Windows) or in
+your home directory (on Linux): If you put it somewhere else, edit
+setpaths or setpaths.bat, as appropriate. Once all this is done, run
+<code>setpaths.bat</code> or <code>source setpaths</code> to export
+some other needed environment variables. </p>
+
+<p> All the files in the program are listed in base.lst, including
+test cases and an empty answer aspect,
+<code>answers/Answer.java</code>. Therefore, if you write your
+answers there, all you need to do is compile base.lst, either in an
+IDE or with </p>
+
+<blockquote><PRE>
+$ ajc -Xlint -argfile base.lst
+</PRE></blockquote>
+
+<p> Before you move onto another exercise, though, make sure to copy
+your answer into a different file so we can discuss the answers
+together:
+</p>
+
+<blockquote><PRE>
+> copy answers/Answer.java answers/2a.java (Windows)
+$ cp answers/Answer.java answers/2a.java (Linux)
+</PRE></blockquote>
+
+<p> If you want to put your answer in a different file, say,
+<code>answers/Answer2a.java</code>, you can compile with </p>
+
+<blockquote><PRE>
+$ ajc -Xlint -argfile base.lst answers/Answer2a.java
+</PRE> </blockquote>
+
+<p> In any case, after building the system, you should invoke Java on
+the compiled test class. On the command-line, this this would be </p>
+
+<blockquote><PRE>
+$ java tests.Test2a
+</PRE> </blockquote>
+
+<p> (For these exercises, when we give examples of execution we will
+show the command-line use, but of course if you are using JBuilder,
+Forte/NetBeans, Emacs, or Eclipse, use the appropriate compile and
+execute tools.) </p>
+
+<p> The default test, <code>tests.Test</code>, performs some
+rudimentary tests on figure elements, and so is a useful test to run
+periodically. Looking at the JUnit tests for each exercise may also
+be helpful. </p>
+
+<p> Again, ae will be looking at some solutions and having discussion,
+which is much more difficult without incremental solutions. So when
+you go from one exercise to the next, make sure to save your work in a
+file and go on to work in a different file, even if you plan to
+duplicate some code. </p>
+
+<hr>
+<!-- page break -->
+<h2>1. Static Invariants</h2>
+
+<h3>a. Catch old tracing</h3>
+
+<p> The way that we are all taught to print "hello world" from Java is
+to use <code>System.out.println()</code>, so that is what we typically
+use for one-off debugging traces. It's a common mistake to leave
+these in your system longer than is necessary. Type in the aspect
+below to forces an error at compile time if this mistake is made.
+</p>
+
+<p> When you use this on the given system, you'll find one incorrect
+trace in <code>SlothfulPoint</code>.
+</p>
+
+<blockquote><PRE>
+$ ajc -argfile base.lst
+./figures/SlothfulPoint.java:29:9: illegal access to System.out
+ System.out.println("Slothful moving");
+ ^
+1 errors
+</PRE></blockquote>
+
+<p> Remove the illegal tracing call.
+</p>
+
+<p> Make sure your program still passes the JUnit test
+<code>tests.Test</code> (which it should also pass at the beginning of
+all exercises) before continuing. </p>
+
+<blockquote><PRE>
+$ java tests.Test
+....
+Time: 0.076
+
+OK (4 tests)
+</PRE></blockquote>
+
+<p> <strong>Answer:</strong>
+</p>
+
+<blockquote><PRE>
+package answers;
+
+import figures.*;
+
+aspect Answer1a {
+ declare error
+ : get(java.io.PrintStream System.out) && within(figures..*)
+ : "illegal access to System.out";
+}
+</PRE></blockquote>
+
+<p> Note that this answer does not say that the <em>call</em> to the
+<code>println()</code> method is incorrect, rather, that the field get
+of the <code>out</code> field is illegal. This will also catch those
+users who bind System.out to a static field to save typing. </p>
+
+
+<h3>b. Mandate setters</h3>
+
+<p> One common coding convention is that no private field should be
+set outside of setter methods. Write an aspect to warn at compile
+time when such an illegal assignment expression exists. </p>
+
+<p> This is going to look like
+</p>
+
+<pre>
+aspect A {
+ declare warning: <em><pointcut here></em> : "bad field set";
+}
+</pre>
+
+<p> where the pointcut picks out join points of private field sets
+outside of setter methods. "Outside", here, means that the code for
+the assignment is outside the <em>text</em> of the setter (a setter is
+a method that looks like "void set*(..)"), so check out the quick
+reference for <code>set</code> and <code>withincode</code> primitive
+pointcuts. ) </p>
+
+<p> Make sure your program still passes the JUnit test
+<code>tests.Test</code> before continuing, and that you see all of the
+following warning messages. You'll notice a LOT of warnings here.
+Wait to fix them until the next exercise...
+</p>
+
+<pre>
+.\figures\Box.java:17:9: bad field set (warning)
+ _p0 = new Point(x0, y0);
+ ^
+.\figures\Box.java:18:9: bad field set (warning)
+ _p1 = new Point(x0+width, y0);
+ ^
+.\figures\Box.java:19:9: bad field set (warning)
+ _p2 = new Point(x0+width, y0+height);
+ ^
+.\figures\Box.java:20:9: bad field set (warning)
+ _p3 = new Point(x0, y0+height);
+ ^
+.\figures\Group.java:16:23: bad field set (warning)
+ this._members = new ArrayList();
+ ^
+.\figures\Line.java:15:9: bad field set (warning)
+ _p1 = p1;
+ ^
+.\figures\Line.java:16:9: bad field set (warning)
+ _p2 = p2;
+ ^
+.\figures\Point.java:15:9: bad field set (warning)
+ _x = x;
+ ^
+.\figures\Point.java:16:9: bad field set (warning)
+ _y = y;
+ ^
+.\figures\Point.java:28:9: bad field set (warning)
+ _x += dx;
+ ^
+.\figures\Point.java:29:9: bad field set (warning)
+ _y += dy;
+ ^
+</pre>
+
+
+<h3>c. Refine setters mandate</h3>
+
+<p> Look at some of the warnings. Notice that a lot of them are from
+within constructors. Actually, the common coding convention is that
+no private field should be set outside of setter methods <b>or
+constructors</b>. Modify your answer (in a new file) to signal an
+actual error at compile time (rather than just a warning) when such an
+illegal assignment expression exists. </p>
+
+<p>You'll want to add another <code>withincode</code> primitive
+pointcut to deal with the constructors.
+</p>
+
+<p>After you specify your pointcut correctly, you'll still find that
+the convention is violated twice in the figures package. You should see
+the following two errors:</p>
+
+<pre>
+.\figures\Point.java:28:9: bad field set
+ _x += dx;
+ ^
+.\figures\Point.java:29:9: bad field set
+ _y += dy;
+ ^
+2 errors
+</pre>
+
+<p> (If you see more, go back to 1b; you may be capturing sets to
+too many fields.)
+</p>
+
+<p>Rewrite these two occurrences so as not to violate
+the convention. Make sure your program still passes the JUnit test
+<code>tests.Test</code> before continuing. </p>
+
+<h3>d. Re-refine setters mandate</h3>
+
+<p> In part (c), you rewrote the code to fit the convention enforced
+by the aspect. It may be that this code doesn't violate the convention
+of your mythical organization. Try to instead fix the pointcut so it
+doesn't signal an error for these two assignments, and then change
+your code back to making the assignments. </p>
+
+<p> Make sure your program still passes the JUnit test
+<code>tests.Test</code> before continuing. </p>
+
+<h3>Help Yourself by Helping Others</h3>
+
+<p> At this point, check the people to your left and right. If
+they're stuck somewhere, see if you can help them. </p>
+
+
+<!-- ============================== -->
+
+<hr />
+
+<h2>2. Dynamic invariants</h2>
+
+
+<h3>a. Check a simple precondition</h3>
+
+<p> Write an aspect to throw an <code>IllegalArgumentException</code>
+whenever an attempt is made to set one of <code>Point</code>'s
+<code>int</code> fields to a value that is either too large (greater
+than <code>FigureElement.MAX_VALUE</code>) or too small (less than
+<code>FigureElement.MIN_VALUE</code>). </p>
+
+<p> This should make the test case of <code>tests.Test2a</code> pass,
+which wouldn't without your aspect. So before compiling in the
+aspect,
+</p>
+
+<blockquote><PRE>
+$ ajc -Xlint -argfile base.lst
+
+$ java tests.Test2a
+.F.F.F....
+Time: 0.099
+There were 3 failures:
+1) testTooSmall(tests.Test2a)junit.framework.AssertionFailedError: should have thrown IllegalArgumentException
+2) testTooBig(tests.Test2a)junit.framework.AssertionFailedError: should have thrown IllegalArgumentException
+3) testMove(tests.Test2a)junit.framework.AssertionFailedError: should have thrown IllegalArgumentException
+
+FAILURES!!!
+Tests run: 7, Failures: 3, Errors: 0
+</PRE></blockquote>
+
+<p> But after compiling in the aspect...
+</p>
+
+<blockquote><PRE>
+$ ajc -Xlint -argfile base.lst
+
+$ java tests.Test2a
+.......
+Time: 0.097
+
+OK (7 tests)
+</PRE></blockquote>
+
+<p> <strong>Answer:</strong>
+</p>
+
+<blockquote><PRE>
+package answers;
+
+import figures.*;
+
+aspect Answer2a {
+ before(int newValue): set(int Point.*) && args(newValue) {
+ if (newValue < FigureElement.MIN_VALUE) {
+ throw new IllegalArgumentException("too small");
+ } else if (newValue > FigureElement.MAX_VALUE) {
+ throw new IllegalArgumentException("too large");
+ }
+ }
+}
+</PRE></blockquote>
+
+<h3>b. Check another precondition</h3>
+
+<p> <code>Group</code> is a <code>FigureElement</code> class that
+encapsulates groups of other figure elements. As such, only actual
+figure element objects should be added to <code>Group</code> objects. Write
+an aspect to throw an <code>IllegalArgumentException</code> whenever
+<code>Group.add()</code> is called with a
+<code>null</code> value. If you look at the source code for
+tests/Test2b.java, you'll see an example of the desired behavior,
+i.e. </p>
+
+<pre>
+ public void testNull() {
+ try {
+ g.add(null);
+ fail("should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException ea) {
+ }
+ }
+</pre>
+
+<p> For each of these exercises, you'll find that the corresponding
+test case provides that most concrete example of the desired behavior
+for your aspect. Please avail yourself of this resource. </p>
+
+<p> With this aspect in place, your code should pass
+<code>tests.Test2b</code>.
+</p>
+
+<h3>c. Check yet another precondition</h3>
+
+<p> Another constraint on a well-formed group is that it should not
+contain itself as a member (though it may contain other groups). Write
+an aspect to throw an <code>IllegalArgumentException</code> whenever
+an attempt is made to call <code>Group.add()</code> on a
+<code>null</code> value, or on the group itself. </p>
+
+<p> You will want to use a <code>target</code> pointcut to expose the
+<code>Group</code> object that is the target of the <code>add</code>
+call.
+</p>
+
+<p> With this aspect in place, your code should pass
+<code>tests.Test2c</code>.
+</p>
+
+<h3>d. Check a simple postcondition</h3>
+
+<p> One of the simplest postconditions to check is that a setter
+actually sets its value. Write an aspect that throws a
+<code>java.lang.RuntimeException</code> if, after calling
+<code>setX()</code> on <code>SlothfulPoint</code> objects,
+<code>getX()</code> doesn't return the new value. </p>
+
+<p> You'll want to use an <code>args</code> pointcut to expose the
+argument to <code>setX()</code> and a <code>target</code> pointcut to
+expose the <code>SlothfulPoint</code> object itself (so you can later
+call <code>getX()</code> on it).
+</p>
+
+<p> An interesting question to think about for discussion is whether
+this postcondition should apply when getX() throws an exception, or
+when it returns normally, or both? </p>
+
+<p> With this aspect in place, your code should pass
+<code>tests.Test2d</code>.
+</p>
+
+<h3>e. Check invariant</h3>
+
+<p> There is a method on the <code>Box</code> class, <code>void
+checkBoxness()</code>, that checks whether the four points making up a
+box are correctly positioned relative to each other (i.e., they form a
+rectangle). Write an aspect that will make sure that after every time
+the <code>void move(int, int)</code> method on <code>Box</code> is
+called, that you also call <code>Box.checkBoxness()</code> to ensure
+that the <code>move</code> didn't break this invariant. </p>
+
+<p> With this aspect in place, your code should pass
+<code>tests.Test2e</code>.
+</p>
+
+<h3>f. Refine your invariant</h3>
+
+<p> <code>move</code> is not the only interesting method on
+<code>Box</code>. It may be that a box gets malformed between calls
+to <code>move</code>. So instead of checking boxness only
+after the <code>move</code> method of <code>Box</code>, check
+after the call to every one of <code>Box</code>'s public methods.
+</p>
+
+<p> When testing this aspect, you may find yourself facing a
+<code>StackOverflowException</code>. If so, carefully look at your
+pointcuts. Needless to say, there should not be an infinite loop in
+your program. You might want to look at using a <code>within</code>
+pointcut for a filter. </p>
+
+<p> (You might even find that this test case aborts with no message,
+i.e.,
+</p>
+
+<blockquote><pre>
+$ java tests.test2f
+.
+$
+</pre></blockquote>
+
+<p> this is a bug in Sun's JVM where a particular stack overflow
+causes the VM to abort.)
+</p>
+
+<p> Make sure to pass the JUnit test <code>tests.Test2f</code>
+before continuing. </p>
+
+
+<h3>g. Assure input</h3>
+
+<p> Instead of throwing an exception when one of <code>Point</code>'s
+<code>int</code> fields are set to an out-of-bounds value, write an
+aspect to trim the value into an in-bounds one. You'll want to use
+<code>around</code> advice that exposes the new value of the field
+assignment with an <code>args</code> pointcut, and
+<code>proceed</code> with the trimmed value. Becuase this is tricky,
+type in the below aspect... the trick for this exercise is not to come
+up with an answer, but to understand the answer. </p>
+
+<p> Make sure to pass the JUnit test <code>tests.Test2g</code>
+before continuing. </p>
+
+<p> <strong>Answer: </strong>
+</p>
+
+<blockquote><PRE>
+package answers;
+
+import figures.*;
+
+aspect Answer2g {
+ int around(int val):
+ (set(int Point._x) || set(int Point._y))
+ && args(val) {
+ return proceed(trim(val));
+ }
+
+ private int trim(int val) {
+ return Math.max(Math.min(val, FigureElement.MAX_VALUE),
+ FigureElement.MIN_VALUE);
+ }
+}
+</PRE></blockquote>
+
+
+<h3>h. Check another invariant</h3>
+
+<p> <code>FigureElement</code> objects have a <code>getBounds()</code>
+method that returns a <code>java.awt.Rectangle</code> representing the
+bounds of the object. An important postcondition of the
+<code>move</code> operation is that the figure element's bounds
+rectangle should move by the same amount as the figure itself. Write
+an aspect to check for this postcondition -- throw an
+<code>IllegalStateException</code> if it is violated. </p>
+
+<p> Note that because we're dealing with how the bounds changes during
+move, we need some way of getting access to the bounds both before
+<em>and</em> after the move, in one piece of advice. Also, note that
+you can create a copy of a figure element's bounds rectangle with
+<code>new Rectangle(fe.getBounds())</code>, and you can move a bounds
+rectangle <code>rect</code> with <code>rect.translate(dx,
+dy)</code>. </p>
+
+<p> Make sure to pass the JUnit test <code>tests.Test2h</code>
+before continuing. </p>
+
+
+<h3>Help Yourself by Helping Others</h3>
+
+<p> At this point, check the people to your left and right. If
+they're stuck somewhere, see if you can help them. </p>
+
+<!-- ============================== -->
+
+<hr />
+<!-- page break -->
+
+<h2>3. Tracing</h2>
+
+<p> The crosscutting feature you will be adding in part (4) will be
+support for caching the bound objects of <code>Group</code> figure
+elements, which may be costly to compute. On the way to that, though,
+it's useful to explore the system with some tracing aspects. </p>
+
+<h3>a. Simple tracing</h3>
+
+<p> Write an aspect to trace whenever a <code>Point</code> is moved.
+To do this, use the utility class <code>Log</code> (with an import
+from <code>support.Log</code>) and call </p>
+
+<blockquote><PRE>
+Log.log("moving")
+</PRE></blockquote>
+
+<p> This will write the string "moving", followed by a semicolon
+terminator, to the Log. For example, with your aspect enabled,
+</p>
+
+<blockquote><PRE>
+Point p1 = new Point(10, 100);
+p1.move(37, 8);
+System.out.println(Log.getString());
+</PRE></blockquote>
+
+<p> should print out "moving;".
+</p>
+
+<p> Test this with the JUnit test case <code>tests.Test3a</code>.
+Without adding any aspects, this test should fail: </p>
+
+<blockquote><PRE>
+$ ajc -Xlint -argfile base.lst
+$ java tests.Test3a
+..F.......
+Time: 0.07
+There was 1 failure:
+1) testMovePointLog(tests.Test3a)junit.framework.AssertionFailedError: expected:<set;> but was:<>
+ at tests.Test3a.testMovePointLog(Test1a.java:30)
+ at tests.Test3a.main(Test1a.java:16)
+
+FAILURES!!!
+Tests run: 9, Failures: 1, Errors: 0
+</PRE></blockquote>
+
+<p> But with the proper aspect added to the compilation, (in this
+case, <code>answers/Answer3a.java</code>, but you should feel free to
+use more evocative names), the test should pass </p>
+
+<blockquote><PRE>
+$ ajc -Xlint -argfile base.lst answers/Answer3a.java
+$ java tests.Test3a
+.........
+Time: 0.089
+
+OK (9 tests)
+</PRE></blockquote>
+
+<p> <strong>Answer: </strong>
+</p>
+
+<blockquote><PRE>
+package answers;
+
+import support.Log;
+import figures.*;
+
+aspect Answer3a {
+ before(): execution(void Point.move(int, int)) {
+ Log.log("moving");
+ }
+}
+</PRE></blockquote>
+
+<h3>b. More complex tracing</h3>
+
+<p> Write an aspect to trace whenever a <code>Point</code> is added to
+a group (including initially). To do this, use the utility class
+<code>Log</code> (with an import from <code>support.Log</code>) and
+call </p>
+
+<blockquote><PRE>
+Log.log("adding Point")
+</PRE></blockquote>
+
+<p> This will write the string "adding Point", followed by a semicolon
+terminator, to the Log. For example, with your aspect enabled, </p>
+
+<blockquote><PRE>
+Point p1 = new Point(10, 100);
+Point p2 = new Point(10, 100);
+Group g = new Group(p1);
+g.add(p2);
+System.out.println(Log.getString());
+</PRE></blockquote>
+
+<p> should print out "adding Point;adding Point;".
+</p>
+
+<p> <em>Hint: The <code>args</code> pointcut allows you to select join points
+based on the type of a parameter to a method call. </em> </p>
+
+<p> Test this with the JUnit test case <code>tests.Test3b</code>.
+
+
+<h3>c. Keeping track of state</h3>
+
+<p> In this exercise, perform the tracing from part (a), but also log
+the enclosing group, if any, of the moving point. You can use an
+inter-type declaration inside your aspect to associate a
+<code>Group</code> field with <code>Point</code> objects, and then
+work with that field, setting it appropriately when the
+<code>Point</code> is added to a <code>Group</code> (at the same join
+points you were tracing in part b). So </p>
+
+<blockquote><PRE>
+Point p1 = new Point(10, 100);
+p1.move(0, 0);
+System.out.println(Log.getString());
+</PRE></blockquote>
+
+<p> should print out "moving as a part of null;", but
+</p>
+
+<blockquote><PRE>
+Point p1 = new Point(10, 100);
+Group g = new Group(p1);
+p1.move(0, 0);
+System.out.println(Log.getString());
+</PRE></blockquote>
+
+<p> should print out "moving as a part of Group(Point(10, 100));",
+which you can do by using the toString() method already defined on
+Group. </p>
+
+<p> <em> Hint: This exercise combines the tracing from parts a and b.
+If you start with an aspect that includes the solutions to those
+previous exercises, you'll be most of the way there. </em> </p>
+
+<p> Test this with the JUnit test case <code>tests.Test3c</code>.
+
+
+<h3>Help Yourself by Helping Others</h3>
+
+<p> At this point, check the people to your left and right. If
+they're stuck somewhere, see if you can help them. </p>
+
+
+<!-- ============================== -->
+
+<hr />
+
+<!-- page break -->
+<h3>4. Caching</h3>
+
+<p> Computation of the bounding box of <code>Group</code> objects
+needs to deal with all aggregate parts of the group, and this
+computation can be expensive. In this section, we will explore
+various ways of reducing this expense. </p>
+
+<p> <strong>Optional</strong>: In all of these exercises, you should
+only deal with points that are added directly to Groups, rather than
+those that are added "indirectly" through Lines and Boxes. You should
+handle those points contained in Lines and Boxes only if time permits.
+</p>
+
+<h3>a. Make a constant override</h3>
+
+<p> <code>Group</code>'s <code>getBounds()</code> method could be
+understood to be a conservative approximation of the bounding box of a
+group. If that is true, then it would be a legal (and much faster)
+implementation of <code>getBounds()</code> to simply always return a
+rectangle consisting of the entire canvas, that is
+</p>
+
+<blockquote><PRE>
+new Rectangle(FigureElement.MIN_VALUE, FigureElement.MIN_VALUE,
+ FigureElement.MAX_VALUE - FigureElement.MIN_VALUE,
+ FigureElement.MAX_VALUE - FigureElement.MIN_VALUE)
+</PRE></blockquote>
+
+<p> Write an aspect to implement this change. You can override
+<code>Group</code>'s <code>getBounds()</code> method entirely with
+around advice intercepting the method.
+</p>
+
+<p> Your code should pass the JUnit test case
+<code>tests.Test4a</code> with this change.
+</p>
+
+<p> <strong>Answer: </strong>
+</p>
+
+<blockquote><PRE>
+package answers;
+
+import figures.*;
+import java.awt.Rectangle;
+
+aspect Answer4a {
+ private Rectangle wholeCanvas =
+ new Rectangle(FigureElement.MIN_VALUE, FigureElement.MIN_VALUE,
+ FigureElement.MAX_VALUE - FigureElement.MIN_VALUE,
+ FigureElement.MAX_VALUE - FigureElement.MIN_VALUE);
+
+ Rectangle around(): execution(Rectangle Group.getBounds()) {
+ return wholeCanvas;
+ }
+}
+</PRE></blockquote>
+
+<h3>b. Make a constant cache</h3>
+
+<p> Instead of making the (very) conservative approximation of
+<code>getBounds()</code> from part (a), write an aspect instead that
+remembers the return value from the first time
+<code>getBounds()</code> has been called on a <code>Group</code>, and
+returns that first <code>Rectangle</code> for every subsequent
+call. </p>
+
+<p> <em>Hint: You can use an inter-type declaration to keep some
+state for every <code>Group</code> object.</em> </p>
+
+<p> Your code should pass the JUnit test case
+<code>tests.Test4b</code> with this change.
+</p>
+
+
+<h3>c. Invalidate, part 1</h3>
+
+<p> While caching in this way does save computation, it will lead to
+incorrect bounding boxes if a <code>Group</code> is ever moved.
+Change your aspect so that it invalidates the cache whenever the
+<code>move()</code> method of <code>Group</code> is called.
+</p>
+
+<p> Your code should pass the JUnit test case
+<code>tests.Test4c</code> with this change.
+</p>
+
+<h3>d. Invalidate, part 2</h3>
+
+<p> Of course, part (c) didn't really solve the problem. What if a
+<code>Point</code> that is part of a <code>Group</code> moves?
+Whenever either of a Point's fields are set it should invalidate the
+caches of all enclosing groups. Use your solution to problem 3c to
+modify your invalidation criteria in this way, but note that this is
+slightly different than the problem in 3c: Here you care about fields,
+where there you cared about method calls. </p>
+
+<p> Your code should pass the JUnit test case
+<code>tests.Test4d</code> with this change.
+</p>
+
+<h3>e. Invalidate, part 3</h3>
+
+<p> Did you really do part (d) correctly? Run the JUnit test
+<code>tests.Test4e</code> to see. If you pass, congratulations, now
+go help other people. Otherwise, you have fallen prey to our cruel
+trap: Remember that whenever a point moves it should invalidate the
+caches of <em>all</em> enclosing groups. </p>
+
+<hr>
+</body> </html>
--- /dev/null
+Dear OOPSLA 2002 attendee;
+
+We have you listed as being registered for tutorial T40,
+Aspect-oriented programming with AspectJ. We are excited about giving
+this tutorial, and hope you will enjoy the presentation, exercises and
+discussion we have prepared.
+
+As with our past tutorials of this form, in the afternoon we would
+like to break the attendees into groups of two or three and work
+through a number of AspectJ exercises together.
+
+We will be bringing media and making ourselves available during breaks
+for help with setup, but in order to jump straight in and give you the
+most we can from this tutorial, it would really help us if many of you
+had an AspectJ environment installed early.
+
+This message contains basic instructions on where to get some needed
+tools. These instructions will not take much time.
+
+If you are planning to bring a laptop to the tutorial, would you
+please take the time to do the steps outlined in this message?
+
+If you're not planning to, you might want to install an AspectJ
+environment on your desktop anyway and try the instructions below, so
+you will be comfortable when we meet on Wednesday.
+
+[If you already have a working AspectJ environment and are familliar
+with it, we still recommend that you upgrade to 1.0.6 and follow the
+steps below]
+
+Thank you, and please don't hesitate to contact us (at
+support@aspectj.org) if you have any questions. See you on
+Wednesday...
+
+-Erik Hilsdale,
+ Jim Hugunin,
+ and the whole AspectJ Team
+
+
+Getting Ready for T40, Aspect-oriented programming with AspectJ
+--------------------------------------
+
+Overview:
+
+ 0. Install AspectJ
+ 1. Download JUnit and put it on your classpath
+ 2. Test your setup
+
+------------------------------
+0. AspectJ
+
+Download the AspectJ 1.0.6 from
+
+ http://aspectj.org/dl
+
+You should definitly download and intstall the tools package and the
+docs package. If you plan to use JBuilder, Forte/NetBeans, Emacs, or
+Eclipse for your development, you should download the appropriate
+plugin.
+
+
+------------------------------
+1. JUnit
+
+We use the JUnit framework for testing our exercises. Download JUnit
+from
+
+ http://www.junit.org
+
+and place junit.jar on your CLASSPATH.
+
+
+------------------------------
+2. Test your setup
+
+a. Create a file "Hello.java" with this class:
+
+ class Hello {
+ public static void main(String[] args) {
+ System.err.println(getHelloString());
+ }
+ public static String getHelloString() {
+ return "Hello, WORLD";
+ }
+ }
+
+b. Compile the class with ajc and run it...
+
+ > ajc Hello.java
+ > java Hello
+ Hello, WORLD
+
+c. Create a file "TestHello.java" with this class:
+
+ public class TestHello extends junit.framework.TestCase {
+ public TestHello(String name) {
+ super(name);
+ }
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(TestHello.class);
+ }
+ public void testHello() {
+ assertEquals("Hello, OOPSLA", Hello.getHelloString());
+ }
+ }
+
+d. Compile the class with ajc and run it...
+
+ > ajc TestHello.java
+ > java TestHello
+ .F
+ Time: 0.01
+ There was 1 failure:
+ 1) testHello(TestHello)junit.framework.ComparisonFailure:
+ expected:<...OOPSLA> but was:<...WORLD>
+ at TestHello.testHello(TestHello.java:9)
+ at TestHello.main(TestHello.java:6)
+
+ FAILURES!!!
+ Tests run: 1, Failures: 1, Errors: 0
+
+e. Oops... the test case seems to want a different string than the
+ tested class. Fix that, compile whichever file you changed with
+ ajc, run the tester again, and you're done. Thanks!
+
+ > java TestHello
+ .
+ Time: 0
+
+ OK (1 test)
--- /dev/null
+#!/bin/sh\r
+\r
+# Change this to be the full path for this directory\r
+EXTRACTION=$HOME/aj-oopsla\r
+\r
+if [ "$JAVA_HOME" = "" ] \r
+then\r
+ echo Please remember to manually set $JAVA_HOME to \r
+ echo the location of your java installation\r
+fi\r
+\r
+export ASPECTJ_HOME=$EXTRACTION/aspectj\r
+export PATH=$ASPECTJ_HOME/bin:$PATH\r
+export CLASSPATH=.:$ASPECTJ_HOME/lib/aspectjrt.jar:$EXTRACTION/junit.jar\r
--- /dev/null
+@echo off\r
+\r
+rem Change this to be the full path for this directory\r
+set EXTRACTION=c:\aj-oopsla\r
+\r
+if exist "%JAVA_HOME%\bin\java.exe" goto haveJava\r
+if exist "%JAVA_HOME%\bin\java.bat" goto haveJava\r
+if exist "%JAVA_HOME%\bin\java" goto haveJava\r
+echo java does not exist as %JAVA_HOME%\bin\java\r
+echo please fix the JAVA_HOME environment variable\r
+\r
+:haveJava\r
+set ASPECTJ_HOME=%EXTRACTION%\aspectj\r
+set PATH=%ASPECTJ_HOME%\bin;%PATH%\r
+set CLASSPATH=.;%ASPECTJ_HOME%\lib\aspectjrt.jar;%EXTRACTION%\junit.jar\r
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+
+package support;
+
+public class Log {
+ static StringBuffer data = new StringBuffer();
+
+ public static void traceObject(Object o) {
+ throw new UnsupportedOperationException();
+ }
+
+ public static void log(String s) {
+ data.append(s);
+ data.append(';');
+ }
+
+ public static void logClassName(Class _class) {
+ String name = _class.getName();
+ int dot = name.lastIndexOf('.');
+ if (dot == -1) {
+ log(name);
+ } else {
+ log(name.substring(dot+1, name.length()));
+ }
+ }
+
+ public static String getString() {
+ return data.toString();
+ }
+
+ public static void clear() {
+ data.setLength(0);
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package tests;
+
+import figures.*;
+
+import junit.framework.*;
+
+public class Test extends TestCase {
+ public Test(String name) { super(name); }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(Test.class);
+ }
+
+ Box bb;
+ Point p1;
+ Point p2;
+ Line l1;
+ SlothfulPoint sloth1;
+ Group g;
+
+ public void setUp() {
+ p1 = new Point(10, 100);
+ p2 = new Point(20, 200);
+ l1 = new Line(p1, p2);
+ bb = new Box(5, 5, 10, 10);
+ sloth1 = new SlothfulPoint(0, 0);
+ g = new Group(p1);
+ }
+
+ public final void testCreate() {
+ assertEquals(p1.getX(), 10);
+ assertEquals(p1.getY(), 100);
+
+ assertEquals(l1.getP1(), p1);
+ assertEquals(l1.getP2(), p2);
+ }
+
+ public final void testSetPoint() {
+ p1.setX(20);
+ assertEquals(p1.getX(), 20);
+ assertEquals(p1.getY(), 100);
+
+ p1.setY(10);
+ assertEquals(p1.getX(), 20);
+ assertEquals(p1.getY(), 10);
+ }
+
+ public final void testMoveLine1() {
+ l1.move(40, 40);
+ assertEquals(l1.getP1(), p1);
+ assertEquals(l1.getP2(), p2);
+
+ assertEquals(p1.getX(), 50);
+ assertEquals(p1.getY(), 140);
+
+ assertEquals(p2.getX(), 60);
+ assertEquals(p2.getY(), 240);
+ }
+
+ public final void testMoveLine2() {
+ l1.move(-10, -10);
+ assertEquals(p1.getX(), 0);
+ assertEquals(p1.getY(), 90);
+
+ assertEquals(p2.getX(), 10);
+ assertEquals(p2.getY(), 190);
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package tests;
+
+import figures.*;
+import support.Log;
+
+import junit.framework.*;
+
+public class Test1a extends Test {
+ public Test1a(String name) { super(name); }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(Test1a.class);
+ }
+
+ public void setUp() {
+ Log.clear();
+ super.setUp();
+ }
+
+ public void testCreateLog() {
+ assertEquals("", Log.getString());
+ }
+
+ public void testSetXPointLog() {
+ p1.setX(20);
+ assertEquals("set;", Log.getString());
+ }
+
+ public void testSetYPointLog() {
+ p1.setY(10);
+ assertEquals("", Log.getString());
+ }
+
+ public void testGetYPointLog() {
+ p1.getY();
+ assertEquals("", Log.getString());
+ }
+
+ public void testMoveLineLog() {
+ l1.move(40, 40);
+ assertEquals("", Log.getString());
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package tests;
+
+import figures.*;
+
+import junit.framework.*;
+
+public class Test2a extends Test {
+ public Test2a(String name) { super(name); }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(Test2a.class);
+ }
+
+ public void setUp() {
+ super.setUp();
+ }
+
+ public void testTooSmall() {
+ try {
+ p1.setX(-10);
+ fail("should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException ea) {
+ }
+ }
+
+
+ public void testTooBig() {
+ try {
+ p1.setY(1000);
+ fail("should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException ea) {
+ }
+ }
+
+
+ public void testMove() {
+ try {
+ l1.move(-500, -500);
+ fail("should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException ea) {
+ }
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package tests;
+
+import figures.*;
+
+import junit.framework.*;
+
+public class Test2b extends Test {
+ public Test2b(String name) { super(name); }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(Test2b.class);
+ }
+
+ public void setUp() {
+ super.setUp();
+ }
+
+ public void testNull() {
+ try {
+ g.add(null);
+ fail("should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException ea) {
+ }
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package tests;
+
+import figures.*;
+
+import junit.framework.*;
+
+public class Test2c extends Test {
+ public Test2c(String name) { super(name); }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(Test2c.class);
+ }
+
+ public void setUp() {
+ super.setUp();
+ }
+
+ public void testNull() {
+ try {
+ g.add(null);
+ fail("should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException ea) {
+ }
+ }
+
+ public void testSelf() {
+ try {
+ g.add(g);
+ fail("should have thrown IllegalArgumentException");
+ } catch (IllegalArgumentException ea) {
+ }
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package tests;
+
+import figures.*;
+
+import junit.framework.*;
+
+public class Test2d extends Test {
+ public Test2d(String name) { super(name); }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(Test2d.class);
+ }
+
+ public void setUp() {
+ super.setUp();
+ }
+
+ public void testSetting() {
+ try {
+ sloth1.setX(10);
+ fail("should have thrown RuntimeException");
+ } catch (RuntimeException ea) {
+ }
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package tests;
+
+import figures.*;
+
+import junit.framework.*;
+
+public class Test2e extends Test {
+ public Test2e(String name) { super(name); }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(Test2e.class);
+ }
+
+ public void setUp() {
+ super.setUp();
+ }
+
+ public void testEasy() {
+ Box sq = new Box(0, 0, 10, 10);
+ sq.move(5,5);
+ assertEquals(sq.getP0().getX(), 5);
+ assertEquals(sq.getP0().getY(), 5);
+
+ try {
+ sq.getP0().setX(100);
+ sq.move(37, 1);
+ fail("should have thrown IllegalStateException");
+ } catch (IllegalStateException e) { }
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package tests;
+
+import figures.*;
+
+import junit.framework.*;
+
+public class Test2f extends Test {
+ public Test2f(String name) { super(name); }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(Test2f.class);
+ }
+
+ public void setUp() {
+ super.setUp();
+ }
+
+ public void testEasy() {
+ Box sq = new Box(0, 0, 10, 10);
+ sq.move(5,5);
+ assertEquals(sq.getP0().getX(), 5);
+ assertEquals(sq.getP0().getY(), 5);
+
+ try {
+ sq.getP0().setX(100);
+ sq.getP1();
+ fail("should have thrown IllegalStateException");
+ } catch (IllegalStateException e) { }
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package tests;
+
+import figures.*;
+
+import junit.framework.*;
+
+public class Test2g extends Test {
+ public Test2g(String name) { super(name); }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(Test2g.class);
+ }
+
+ public void setUp() {
+ super.setUp();
+ }
+
+ public void testBounds() {
+ p1.setX(FigureElement.MAX_VALUE + 1);
+ assertEquals(FigureElement.MAX_VALUE, p1.getX());
+ p1.setY(FigureElement.MIN_VALUE - 1);
+ assertEquals(FigureElement.MIN_VALUE, p1.getY());
+ }
+
+ public void testBox() {
+ Box s = new Box(50, 50, 20000, 20000);
+ assertEquals(FigureElement.MAX_VALUE, s.getP2().getX());
+ assertEquals(FigureElement.MAX_VALUE, s.getP2().getY());
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package tests;
+
+import figures.*;
+
+import junit.framework.*;
+
+public class Test2h extends Test {
+ public Test2h(String name) { super(name); }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(Test2h.class);
+ }
+
+ public void setUp() {
+ super.setUp();
+ }
+
+ public void testSloth() {
+ FigureElement fe = new SlothfulPoint(10, 10);
+ try {
+ fe.move(10, 10);
+ fail("should have thrown IllegalStateException");
+ } catch (IllegalStateException e) { }
+ }
+
+ public void movePoints() {
+ p1.move(30, 45);
+ p2.move(10, 33);
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package tests;
+
+import figures.*;
+import support.Log;
+
+import junit.framework.*;
+
+public class Test3a extends Test {
+ public Test3a(String name) { super(name); }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(Test3a.class);
+ }
+
+ public void setUp() {
+ Log.clear();
+ super.setUp();
+ }
+
+ public void testCreateLog() {
+ assertEquals("", Log.getString());
+ }
+
+ public void testMovePointLog() {
+ p1.move(20, 30);
+ assertEquals("moving;", Log.getString());
+ }
+
+ public void testSetYPointLog() {
+ assertEquals("", Log.getString());
+ }
+
+ public void testGetYPointLog() {
+ p1.getY();
+ assertEquals("", Log.getString());
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package tests;
+
+import figures.*;
+import support.Log;
+
+import junit.framework.*;
+
+public class Test3b extends Test {
+ public Test3b(String name) { super(name); }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(Test3b.class);
+ }
+
+ public void setUp() {
+ super.setUp();
+ Log.clear();
+ }
+
+ public void testCreateLog() {
+ assertEquals("", Log.getString());
+ }
+
+ public void testCreateWithPointLog() {
+ g = new Group(p1);
+ assertEquals("adding Point;", Log.getString());
+ }
+
+ public void testCreateWithoutPointLog() {
+ g = new Group(l1);
+ assertEquals("", Log.getString());
+ }
+
+ public void testAddPointLog() {
+ g.add(p1);
+ assertEquals("adding Point;", Log.getString());
+ }
+ public void testAddNonPointLog() {
+ g.add(l1);
+ assertEquals("", Log.getString());
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package tests;
+
+import figures.*;
+import support.Log;
+
+import junit.framework.*;
+
+public class Test3c extends Test {
+ public Test3c(String name) { super(name); }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(Test3c.class);
+ }
+
+ public void setUp() {
+ Log.clear();
+ super.setUp();
+ }
+
+ public void testCreateLog() {
+ assertEquals("", Log.getString());
+ }
+
+ public void testMoveLonePoint() {
+ p1 = new Point(0, 0);
+ p1.move(37, 88);
+ assertEquals("moving as a part of null;", Log.getString());
+ }
+
+ public void testMoveGroupedPoint() {
+ g = new Group(p1);
+ p1.move(0, 0);
+ assertEquals("moving as a part of " + g + ";", Log.getString());
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package tests;
+
+import figures.*;
+import java.awt.Rectangle;
+
+import junit.framework.*;
+
+public class Test4a extends Test {
+ Rectangle wholeCanvas =
+ new Rectangle(FigureElement.MIN_VALUE, FigureElement.MIN_VALUE,
+ FigureElement.MAX_VALUE, FigureElement.MAX_VALUE);
+
+ public Test4a(String name) { super(name); }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(Test4a.class);
+ }
+
+ public void setUp() {
+ super.setUp();
+ }
+
+ public void testGroupBounds() {
+ assertEquals(g.getBounds(), wholeCanvas);
+ }
+}
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package tests;
+
+import figures.*;
+import java.awt.Rectangle;
+
+import junit.framework.*;
+
+public class Test4b extends Test {
+
+ public Test4b(String name) { super(name); }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(Test4b.class);
+ }
+
+ public void setUp() {
+ super.setUp();
+ }
+
+ public void testBasicEquality() {
+ assertTrue(g.getBounds() == g.getBounds());
+ }
+
+ public void testEqualityAfterAddition() {
+ Point p0 = new Point(1, 2);
+ Point p1 = new Point(2, 3);
+
+ Group g0 = new Group(p0);
+ Group g1 = new Group(p1);
+
+ Rectangle r0 = g0.getBounds();
+ Rectangle r1 = g1.getBounds();
+ assertTrue(r0 != r1);
+ g0.add(new Point(37, 90));
+ assertTrue(g0.getBounds() == r0);
+ assertTrue(g1.getBounds() != r0);
+ assertTrue(g0.getBounds() != r1);
+ assertTrue(g1.getBounds() == r1);
+
+ g1.add(new Point(4, 8));
+ assertTrue(g0.getBounds() == r0);
+ assertTrue(g1.getBounds() != r0);
+ assertTrue(g0.getBounds() != r1);
+ assertTrue(g1.getBounds() == r1);
+ }
+
+ public void testNotWholeCanvas() {
+ assertTrue("bounds for this group should not be the whole canvas",
+ g.getBounds().getWidth() <
+ (FigureElement.MAX_VALUE - FigureElement.MIN_VALUE));
+ assertTrue("bounds for this group should not be the whole canvas",
+ g.getBounds().getHeight() <
+ (FigureElement.MAX_VALUE - FigureElement.MIN_VALUE));
+
+ }
+}
+
+
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package tests;
+
+import figures.*;
+import java.awt.Rectangle;
+
+import junit.framework.*;
+
+public class Test4c extends Test {
+
+ public Test4c(String name) { super(name); }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(Test4c.class);
+ }
+
+ public void setUp() {
+ super.setUp();
+ }
+
+ public void testBasicEquality() {
+ assertTrue(g.getBounds() == g.getBounds());
+ }
+
+ public void testEqualityAfterAddition() {
+ Point p0 = new Point(1, 2);
+ Point p1 = new Point(2, 3);
+
+ Group g0 = new Group(p0);
+ Group g1 = new Group(p1);
+
+ Rectangle r0 = g0.getBounds();
+ Rectangle r1 = g1.getBounds();
+ assertTrue(r0 != r1);
+ g0.add(new Point(37, 90));
+ assertTrue(g0.getBounds() == r0);
+ assertTrue(g1.getBounds() != r0);
+ assertTrue(g0.getBounds() != r1);
+ assertTrue(g1.getBounds() == r1);
+
+ g1.add(new Point(4, 8));
+ assertTrue(g0.getBounds() == r0);
+ assertTrue(g1.getBounds() != r0);
+ assertTrue(g0.getBounds() != r1);
+ assertTrue(g1.getBounds() == r1);
+ }
+
+ public void testEqualityAfterMove() {
+ Point p0 = new Point(1, 2);
+ Point p1 = new Point(2, 3);
+
+ Group g0 = new Group(p0);
+ Group g1 = new Group(p1);
+
+ Rectangle r0 = g0.getBounds();
+ Rectangle r1 = g1.getBounds();
+ assertTrue(r0 != r1);
+ assertTrue(g0.getBounds() == r0);
+ assertTrue(g1.getBounds() != r0);
+ assertTrue(g0.getBounds() != r1);
+ assertTrue(g1.getBounds() == r1);
+
+ g0.move(3, 1);
+ Rectangle r00 = g0.getBounds();
+ Rectangle r10 = g1.getBounds();
+
+ assertTrue(r10 != r00);
+ assertTrue(r0 != r00);
+ assertTrue(g0.getBounds() == r00);
+ }
+}
+
+
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package tests;
+
+import figures.*;
+import java.awt.Rectangle;
+
+import junit.framework.*;
+
+public class Test4d extends Test {
+
+ public Test4d(String name) { super(name); }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(Test4d.class);
+ }
+
+ public void setUp() {
+ super.setUp();
+ }
+
+ public void testBasicEquality() {
+ assertTrue(g.getBounds() == g.getBounds());
+ }
+
+ public void testNonGroupMove() {
+ p1.move(3, 27);
+ }
+
+ public void testEqualityAfterAddition() {
+ Rectangle r = g.getBounds();
+ g.add(new Point(37, 90));
+ assertTrue(g.getBounds() == r);
+ }
+
+ public void testEqualityAfterMove() {
+ Point p0 = new Point(1, 2);
+ Point p1 = new Point(2, 3);
+
+ Group g0 = new Group(p0);
+ Group g1 = new Group(p1);
+
+ Rectangle r0 = g0.getBounds();
+ Rectangle r1 = g1.getBounds();
+ assertTrue(r0 != r1);
+ assertTrue(g0.getBounds() == r0);
+ assertTrue(g1.getBounds() != r0);
+ assertTrue(g0.getBounds() != r1);
+ assertTrue(g1.getBounds() == r1);
+
+ p0.move(3, 1);
+ Rectangle r00 = g0.getBounds();
+ Rectangle r10 = g1.getBounds();
+
+ assertTrue(r10 != r00);
+ assertTrue(r0 != r00);
+ assertTrue(g0.getBounds() == r00);
+ }
+
+ public void testEqualityAfterMove0() {
+ g = new Group(p1);
+ Rectangle r0 = g.getBounds();
+ assertTrue(g.getBounds() == r0);
+ p1.move(3, 1);
+ Rectangle r1 = g.getBounds();
+ assertTrue(r0 != r1);
+ assertTrue(r1 == g.getBounds());
+ }
+}
+
+
--- /dev/null
+/* *******************************************************************
+ * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
+ * All rights reserved.
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
+ * ******************************************************************/
+
+package tests;
+
+import figures.*;
+import java.awt.Rectangle;
+
+import junit.framework.*;
+
+public class Test4e extends Test {
+
+ public Test4e(String name) { super(name); }
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(Test4e.class);
+ }
+
+ public void setUp() {
+ super.setUp();
+ }
+
+ public void testBasicEquality() {
+ assertTrue(g.getBounds() == g.getBounds());
+ }
+
+ public void testNonGroupMove() {
+ p1.move(3, 27);
+ }
+
+ public void testEqualityAfterAddition() {
+ Rectangle r = g.getBounds();
+ g.add(new Point(37, 90));
+ assertTrue(g.getBounds() == r);
+ }
+
+ public void testEqualityAfterMove() {
+ g = new Group(p1);
+ Rectangle r0 = g.getBounds();
+ assertTrue(g.getBounds() == r0);
+ p1.move(3, 1);
+ Rectangle r1 = g.getBounds();
+ assertTrue(r0 != r1);
+ assertTrue(r1 == g.getBounds());
+ }
+
+ public void testSecondEnclosingGroup() {
+ g = new Group(p1);
+ Group h = new Group(g);
+ Rectangle r0 = h.getBounds();
+ assertTrue(h.getBounds() == r0);
+ p1.move(3, 1);
+ Rectangle r1 = h.getBounds();
+ assertTrue(r0 != r1);
+ assertTrue(r1 == h.getBounds());
+ }
+}
+
+
--- /dev/null
+These presentations (oneHour.ppt, tutorial.ppt, and the teaching materials in exercises) are
+Copyright (c) 1998-2002 Palo Alto Research Center, Incorporated (PARC).
+All rights reserved.
+
+ * This program and the accompanying materials are made available
+ * under the terms of the Common Public License v1.0
+ * which accompanies this distribution and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * PARC initial implementation
\ No newline at end of file