123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769 |
- <!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>
|