You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.html 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
  2. <html> <head>
  3. <title>AspectJ Tutorial Exercises</title>
  4. </head>
  5. <body bgcolor="white">
  6. <h1>AspectJ Tutorial Exercises</h1>
  7. <h3>Organization</h3>
  8. <p> The exercises work with a figure editor together with JUnit test
  9. cases. They progress, as most users do in their adoption of AspectJ,
  10. from non-functional, development-only aspects to aspects which augment
  11. a deployed program with crosscutting features. </p>
  12. <p> We have made available a package that includes the tests, the base
  13. code, JUnit, and a distribution of AspectJ. All it needs is
  14. information about where Java lives (so set your JAVA_HOME environment
  15. variable). It assumes that you unzip it in c:\ (on Windows) or in
  16. your home directory (on Linux): If you put it somewhere else, edit
  17. setpaths or setpaths.bat, as appropriate. Once all this is done, run
  18. <code>setpaths.bat</code> or <code>source setpaths</code> to export
  19. some other needed environment variables. </p>
  20. <p> All the files in the program are listed in base.lst, including
  21. test cases and an empty answer aspect,
  22. <code>answers/Answer.java</code>. Therefore, if you write your
  23. answers there, all you need to do is compile base.lst, either in an
  24. IDE or with </p>
  25. <blockquote><PRE>
  26. $ ajc -Xlint -argfile base.lst
  27. </PRE></blockquote>
  28. <p> Before you move onto another exercise, though, make sure to copy
  29. your answer into a different file so we can discuss the answers
  30. together:
  31. </p>
  32. <blockquote><PRE>
  33. &gt; copy answers/Answer.java answers/2a.java (Windows)
  34. $ cp answers/Answer.java answers/2a.java (Linux)
  35. </PRE></blockquote>
  36. <p> If you want to put your answer in a different file, say,
  37. <code>answers/Answer2a.java</code>, you can compile with </p>
  38. <blockquote><PRE>
  39. $ ajc -Xlint -argfile base.lst answers/Answer2a.java
  40. </PRE> </blockquote>
  41. <p> In any case, after building the system, you should invoke Java on
  42. the compiled test class. On the command-line, this this would be </p>
  43. <blockquote><PRE>
  44. $ java tests.Test2a
  45. </PRE> </blockquote>
  46. <p> (For these exercises, when we give examples of execution we will
  47. show the command-line use, but of course if you are using JBuilder,
  48. Forte/NetBeans, Emacs, or Eclipse, use the appropriate compile and
  49. execute tools.) </p>
  50. <p> The default test, <code>tests.Test</code>, performs some
  51. rudimentary tests on figure elements, and so is a useful test to run
  52. periodically. Looking at the JUnit tests for each exercise may also
  53. be helpful. </p>
  54. <p> Again, ae will be looking at some solutions and having discussion,
  55. which is much more difficult without incremental solutions. So when
  56. you go from one exercise to the next, make sure to save your work in a
  57. file and go on to work in a different file, even if you plan to
  58. duplicate some code. </p>
  59. <hr>
  60. <!-- page break -->
  61. <h2>1. Static Invariants</h2>
  62. <h3>a. Catch old tracing</h3>
  63. <p> The way that we are all taught to print "hello world" from Java is
  64. to use <code>System.out.println()</code>, so that is what we typically
  65. use for one-off debugging traces. It's a common mistake to leave
  66. these in your system longer than is necessary. Type in the aspect
  67. below to forces an error at compile time if this mistake is made.
  68. </p>
  69. <p> When you use this on the given system, you'll find one incorrect
  70. trace in <code>SlothfulPoint</code>.
  71. </p>
  72. <blockquote><PRE>
  73. $ ajc -argfile base.lst
  74. ./figures/SlothfulPoint.java:29:9: illegal access to System.out
  75. System.out.println("Slothful moving");
  76. ^
  77. 1 errors
  78. </PRE></blockquote>
  79. <p> Remove the illegal tracing call.
  80. </p>
  81. <p> Make sure your program still passes the JUnit test
  82. <code>tests.Test</code> (which it should also pass at the beginning of
  83. all exercises) before continuing. </p>
  84. <blockquote><PRE>
  85. $ java tests.Test
  86. ....
  87. Time: 0.076
  88. OK (4 tests)
  89. </PRE></blockquote>
  90. <p> <strong>Answer:</strong>
  91. </p>
  92. <blockquote><PRE>
  93. package answers;
  94. import figures.*;
  95. aspect Answer1a {
  96. declare error
  97. : get(java.io.PrintStream System.out) &amp;&amp; within(figures..*)
  98. : "illegal access to System.out";
  99. }
  100. </PRE></blockquote>
  101. <p> Note that this answer does not say that the <em>call</em> to the
  102. <code>println()</code> method is incorrect, rather, that the field get
  103. of the <code>out</code> field is illegal. This will also catch those
  104. users who bind System.out to a static field to save typing. </p>
  105. <h3>b. Mandate setters</h3>
  106. <p> One common coding convention is that no private field should be
  107. set outside of setter methods. Write an aspect to warn at compile
  108. time when such an illegal assignment expression exists. </p>
  109. <p> This is going to look like
  110. </p>
  111. <pre>
  112. aspect A {
  113. declare warning: <em>&lt;pointcut here&gt;</em> : "bad field set";
  114. }
  115. </pre>
  116. <p> where the pointcut picks out join points of private field sets
  117. outside of setter methods. "Outside", here, means that the code for
  118. the assignment is outside the <em>text</em> of the setter (a setter is
  119. a method that looks like "void set*(..)"), so check out the quick
  120. reference for <code>set</code> and <code>withincode</code> primitive
  121. pointcuts. ) </p>
  122. <p> Make sure your program still passes the JUnit test
  123. <code>tests.Test</code> before continuing, and that you see all of the
  124. following warning messages. You'll notice a LOT of warnings here.
  125. Wait to fix them until the next exercise...
  126. </p>
  127. <pre>
  128. .\figures\Box.java:17:9: bad field set (warning)
  129. _p0 = new Point(x0, y0);
  130. ^
  131. .\figures\Box.java:18:9: bad field set (warning)
  132. _p1 = new Point(x0+width, y0);
  133. ^
  134. .\figures\Box.java:19:9: bad field set (warning)
  135. _p2 = new Point(x0+width, y0+height);
  136. ^
  137. .\figures\Box.java:20:9: bad field set (warning)
  138. _p3 = new Point(x0, y0+height);
  139. ^
  140. .\figures\Group.java:16:23: bad field set (warning)
  141. this._members = new ArrayList();
  142. ^
  143. .\figures\Line.java:15:9: bad field set (warning)
  144. _p1 = p1;
  145. ^
  146. .\figures\Line.java:16:9: bad field set (warning)
  147. _p2 = p2;
  148. ^
  149. .\figures\Point.java:15:9: bad field set (warning)
  150. _x = x;
  151. ^
  152. .\figures\Point.java:16:9: bad field set (warning)
  153. _y = y;
  154. ^
  155. .\figures\Point.java:28:9: bad field set (warning)
  156. _x += dx;
  157. ^
  158. .\figures\Point.java:29:9: bad field set (warning)
  159. _y += dy;
  160. ^
  161. </pre>
  162. <h3>c. Refine setters mandate</h3>
  163. <p> Look at some of the warnings. Notice that a lot of them are from
  164. within constructors. Actually, the common coding convention is that
  165. no private field should be set outside of setter methods <b>or
  166. constructors</b>. Modify your answer (in a new file) to signal an
  167. actual error at compile time (rather than just a warning) when such an
  168. illegal assignment expression exists. </p>
  169. <p>You'll want to add another <code>withincode</code> primitive
  170. pointcut to deal with the constructors.
  171. </p>
  172. <p>After you specify your pointcut correctly, you'll still find that
  173. the convention is violated twice in the figures package. You should see
  174. the following two errors:</p>
  175. <pre>
  176. .\figures\Point.java:28:9: bad field set
  177. _x += dx;
  178. ^
  179. .\figures\Point.java:29:9: bad field set
  180. _y += dy;
  181. ^
  182. 2 errors
  183. </pre>
  184. <p> (If you see more, go back to 1b; you may be capturing sets to
  185. too many fields.)
  186. </p>
  187. <p>Rewrite these two occurrences so as not to violate
  188. the convention. Make sure your program still passes the JUnit test
  189. <code>tests.Test</code> before continuing. </p>
  190. <h3>d. Re-refine setters mandate</h3>
  191. <p> In part (c), you rewrote the code to fit the convention enforced
  192. by the aspect. It may be that this code doesn't violate the convention
  193. of your mythical organization. Try to instead fix the pointcut so it
  194. doesn't signal an error for these two assignments, and then change
  195. your code back to making the assignments. </p>
  196. <p> Make sure your program still passes the JUnit test
  197. <code>tests.Test</code> before continuing. </p>
  198. <h3>Help Yourself by Helping Others</h3>
  199. <p> At this point, check the people to your left and right. If
  200. they're stuck somewhere, see if you can help them. </p>
  201. <!-- ============================== -->
  202. <hr />
  203. <h2>2. Dynamic invariants</h2>
  204. <h3>a. Check a simple precondition</h3>
  205. <p> Write an aspect to throw an <code>IllegalArgumentException</code>
  206. whenever an attempt is made to set one of <code>Point</code>'s
  207. <code>int</code> fields to a value that is either too large (greater
  208. than <code>FigureElement.MAX_VALUE</code>) or too small (less than
  209. <code>FigureElement.MIN_VALUE</code>). </p>
  210. <p> This should make the test case of <code>tests.Test2a</code> pass,
  211. which wouldn't without your aspect. So before compiling in the
  212. aspect,
  213. </p>
  214. <blockquote><PRE>
  215. $ ajc -Xlint -argfile base.lst
  216. $ java tests.Test2a
  217. .F.F.F....
  218. Time: 0.099
  219. There were 3 failures:
  220. 1) testTooSmall(tests.Test2a)junit.framework.AssertionFailedError: should have thrown IllegalArgumentException
  221. 2) testTooBig(tests.Test2a)junit.framework.AssertionFailedError: should have thrown IllegalArgumentException
  222. 3) testMove(tests.Test2a)junit.framework.AssertionFailedError: should have thrown IllegalArgumentException
  223. FAILURES!!!
  224. Tests run: 7, Failures: 3, Errors: 0
  225. </PRE></blockquote>
  226. <p> But after compiling in the aspect...
  227. </p>
  228. <blockquote><PRE>
  229. $ ajc -Xlint -argfile base.lst
  230. $ java tests.Test2a
  231. .......
  232. Time: 0.097
  233. OK (7 tests)
  234. </PRE></blockquote>
  235. <p> <strong>Answer:</strong>
  236. </p>
  237. <blockquote><PRE>
  238. package answers;
  239. import figures.*;
  240. aspect Answer2a {
  241. before(int newValue): set(int Point.*) &amp;&amp; args(newValue) {
  242. if (newValue &lt; FigureElement.MIN_VALUE) {
  243. throw new IllegalArgumentException("too small");
  244. } else if (newValue &gt; FigureElement.MAX_VALUE) {
  245. throw new IllegalArgumentException("too large");
  246. }
  247. }
  248. }
  249. </PRE></blockquote>
  250. <h3>b. Check another precondition</h3>
  251. <p> <code>Group</code> is a <code>FigureElement</code> class that
  252. encapsulates groups of other figure elements. As such, only actual
  253. figure element objects should be added to <code>Group</code> objects. Write
  254. an aspect to throw an <code>IllegalArgumentException</code> whenever
  255. <code>Group.add()</code> is called with a
  256. <code>null</code> value. If you look at the source code for
  257. tests/Test2b.java, you'll see an example of the desired behavior,
  258. i.e. </p>
  259. <pre>
  260. public void testNull() {
  261. try {
  262. g.add(null);
  263. fail("should have thrown IllegalArgumentException");
  264. } catch (IllegalArgumentException ea) {
  265. }
  266. }
  267. </pre>
  268. <p> For each of these exercises, you'll find that the corresponding
  269. test case provides that most concrete example of the desired behavior
  270. for your aspect. Please avail yourself of this resource. </p>
  271. <p> With this aspect in place, your code should pass
  272. <code>tests.Test2b</code>.
  273. </p>
  274. <h3>c. Check yet another precondition</h3>
  275. <p> Another constraint on a well-formed group is that it should not
  276. contain itself as a member (though it may contain other groups). Write
  277. an aspect to throw an <code>IllegalArgumentException</code> whenever
  278. an attempt is made to call <code>Group.add()</code> on a
  279. <code>null</code> value, or on the group itself. </p>
  280. <p> You will want to use a <code>target</code> pointcut to expose the
  281. <code>Group</code> object that is the target of the <code>add</code>
  282. call.
  283. </p>
  284. <p> With this aspect in place, your code should pass
  285. <code>tests.Test2c</code>.
  286. </p>
  287. <h3>d. Check a simple postcondition</h3>
  288. <p> One of the simplest postconditions to check is that a setter
  289. actually sets its value. Write an aspect that throws a
  290. <code>java.lang.RuntimeException</code> if, after calling
  291. <code>setX()</code> on <code>SlothfulPoint</code> objects,
  292. <code>getX()</code> doesn't return the new value. </p>
  293. <p> You'll want to use an <code>args</code> pointcut to expose the
  294. argument to <code>setX()</code> and a <code>target</code> pointcut to
  295. expose the <code>SlothfulPoint</code> object itself (so you can later
  296. call <code>getX()</code> on it).
  297. </p>
  298. <p> An interesting question to think about for discussion is whether
  299. this postcondition should apply when getX() throws an exception, or
  300. when it returns normally, or both? </p>
  301. <p> With this aspect in place, your code should pass
  302. <code>tests.Test2d</code>.
  303. </p>
  304. <h3>e. Check invariant</h3>
  305. <p> There is a method on the <code>Box</code> class, <code>void
  306. checkBoxness()</code>, that checks whether the four points making up a
  307. box are correctly positioned relative to each other (i.e., they form a
  308. rectangle). Write an aspect that will make sure that after every time
  309. the <code>void move(int, int)</code> method on <code>Box</code> is
  310. called, that you also call <code>Box.checkBoxness()</code> to ensure
  311. that the <code>move</code> didn't break this invariant. </p>
  312. <p> With this aspect in place, your code should pass
  313. <code>tests.Test2e</code>.
  314. </p>
  315. <h3>f. Refine your invariant</h3>
  316. <p> <code>move</code> is not the only interesting method on
  317. <code>Box</code>. It may be that a box gets malformed between calls
  318. to <code>move</code>. So instead of checking boxness only
  319. after the <code>move</code> method of <code>Box</code>, check
  320. after the call to every one of <code>Box</code>'s public methods.
  321. </p>
  322. <p> When testing this aspect, you may find yourself facing a
  323. <code>StackOverflowException</code>. If so, carefully look at your
  324. pointcuts. Needless to say, there should not be an infinite loop in
  325. your program. You might want to look at using a <code>within</code>
  326. pointcut for a filter. </p>
  327. <p> (You might even find that this test case aborts with no message,
  328. i.e.,
  329. </p>
  330. <blockquote><pre>
  331. $ java tests.test2f
  332. .
  333. $
  334. </pre></blockquote>
  335. <p> this is a bug in Sun's JVM where a particular stack overflow
  336. causes the VM to abort.)
  337. </p>
  338. <p> Make sure to pass the JUnit test <code>tests.Test2f</code>
  339. before continuing. </p>
  340. <h3>g. Assure input</h3>
  341. <p> Instead of throwing an exception when one of <code>Point</code>'s
  342. <code>int</code> fields are set to an out-of-bounds value, write an
  343. aspect to trim the value into an in-bounds one. You'll want to use
  344. <code>around</code> advice that exposes the new value of the field
  345. assignment with an <code>args</code> pointcut, and
  346. <code>proceed</code> with the trimmed value. Becuase this is tricky,
  347. type in the below aspect... the trick for this exercise is not to come
  348. up with an answer, but to understand the answer. </p>
  349. <p> Make sure to pass the JUnit test <code>tests.Test2g</code>
  350. before continuing. </p>
  351. <p> <strong>Answer: </strong>
  352. </p>
  353. <blockquote><PRE>
  354. package answers;
  355. import figures.*;
  356. aspect Answer2g {
  357. int around(int val):
  358. (set(int Point._x) || set(int Point._y))
  359. &amp;&amp; args(val) {
  360. return proceed(trim(val));
  361. }
  362. private int trim(int val) {
  363. return Math.max(Math.min(val, FigureElement.MAX_VALUE),
  364. FigureElement.MIN_VALUE);
  365. }
  366. }
  367. </PRE></blockquote>
  368. <h3>h. Check another invariant</h3>
  369. <p> <code>FigureElement</code> objects have a <code>getBounds()</code>
  370. method that returns a <code>java.awt.Rectangle</code> representing the
  371. bounds of the object. An important postcondition of the
  372. <code>move</code> operation is that the figure element's bounds
  373. rectangle should move by the same amount as the figure itself. Write
  374. an aspect to check for this postcondition -- throw an
  375. <code>IllegalStateException</code> if it is violated. </p>
  376. <p> Note that because we're dealing with how the bounds changes during
  377. move, we need some way of getting access to the bounds both before
  378. <em>and</em> after the move, in one piece of advice. Also, note that
  379. you can create a copy of a figure element's bounds rectangle with
  380. <code>new Rectangle(fe.getBounds())</code>, and you can move a bounds
  381. rectangle <code>rect</code> with <code>rect.translate(dx,
  382. dy)</code>. </p>
  383. <p> Make sure to pass the JUnit test <code>tests.Test2h</code>
  384. before continuing. </p>
  385. <h3>Help Yourself by Helping Others</h3>
  386. <p> At this point, check the people to your left and right. If
  387. they're stuck somewhere, see if you can help them. </p>
  388. <!-- ============================== -->
  389. <hr />
  390. <!-- page break -->
  391. <h2>3. Tracing</h2>
  392. <p> The crosscutting feature you will be adding in part (4) will be
  393. support for caching the bound objects of <code>Group</code> figure
  394. elements, which may be costly to compute. On the way to that, though,
  395. it's useful to explore the system with some tracing aspects. </p>
  396. <h3>a. Simple tracing</h3>
  397. <p> Write an aspect to trace whenever a <code>Point</code> is moved.
  398. To do this, use the utility class <code>Log</code> (with an import
  399. from <code>support.Log</code>) and call </p>
  400. <blockquote><PRE>
  401. Log.log("moving")
  402. </PRE></blockquote>
  403. <p> This will write the string "moving", followed by a semicolon
  404. terminator, to the Log. For example, with your aspect enabled,
  405. </p>
  406. <blockquote><PRE>
  407. Point p1 = new Point(10, 100);
  408. p1.move(37, 8);
  409. System.out.println(Log.getString());
  410. </PRE></blockquote>
  411. <p> should print out "moving;".
  412. </p>
  413. <p> Test this with the JUnit test case <code>tests.Test3a</code>.
  414. Without adding any aspects, this test should fail: </p>
  415. <blockquote><PRE>
  416. $ ajc -Xlint -argfile base.lst
  417. $ java tests.Test3a
  418. ..F.......
  419. Time: 0.07
  420. There was 1 failure:
  421. 1) testMovePointLog(tests.Test3a)junit.framework.AssertionFailedError: expected:&lt;set;&gt; but was:&lt;&gt;
  422. at tests.Test3a.testMovePointLog(Test1a.java:30)
  423. at tests.Test3a.main(Test1a.java:16)
  424. FAILURES!!!
  425. Tests run: 9, Failures: 1, Errors: 0
  426. </PRE></blockquote>
  427. <p> But with the proper aspect added to the compilation, (in this
  428. case, <code>answers/Answer3a.java</code>, but you should feel free to
  429. use more evocative names), the test should pass </p>
  430. <blockquote><PRE>
  431. $ ajc -Xlint -argfile base.lst answers/Answer3a.java
  432. $ java tests.Test3a
  433. .........
  434. Time: 0.089
  435. OK (9 tests)
  436. </PRE></blockquote>
  437. <p> <strong>Answer: </strong>
  438. </p>
  439. <blockquote><PRE>
  440. package answers;
  441. import support.Log;
  442. import figures.*;
  443. aspect Answer3a {
  444. before(): execution(void Point.move(int, int)) {
  445. Log.log("moving");
  446. }
  447. }
  448. </PRE></blockquote>
  449. <h3>b. More complex tracing</h3>
  450. <p> Write an aspect to trace whenever a <code>Point</code> is added to
  451. a group (including initially). To do this, use the utility class
  452. <code>Log</code> (with an import from <code>support.Log</code>) and
  453. call </p>
  454. <blockquote><PRE>
  455. Log.log("adding Point")
  456. </PRE></blockquote>
  457. <p> This will write the string "adding Point", followed by a semicolon
  458. terminator, to the Log. For example, with your aspect enabled, </p>
  459. <blockquote><PRE>
  460. Point p1 = new Point(10, 100);
  461. Point p2 = new Point(10, 100);
  462. Group g = new Group(p1);
  463. g.add(p2);
  464. System.out.println(Log.getString());
  465. </PRE></blockquote>
  466. <p> should print out "adding Point;adding Point;".
  467. </p>
  468. <p> <em>Hint: The <code>args</code> pointcut allows you to select join points
  469. based on the type of a parameter to a method call. </em> </p>
  470. <p> Test this with the JUnit test case <code>tests.Test3b</code>.
  471. <h3>c. Keeping track of state</h3>
  472. <p> In this exercise, perform the tracing from part (a), but also log
  473. the enclosing group, if any, of the moving point. You can use an
  474. inter-type declaration inside your aspect to associate a
  475. <code>Group</code> field with <code>Point</code> objects, and then
  476. work with that field, setting it appropriately when the
  477. <code>Point</code> is added to a <code>Group</code> (at the same join
  478. points you were tracing in part b). So </p>
  479. <blockquote><PRE>
  480. Point p1 = new Point(10, 100);
  481. p1.move(0, 0);
  482. System.out.println(Log.getString());
  483. </PRE></blockquote>
  484. <p> should print out "moving as a part of null;", but
  485. </p>
  486. <blockquote><PRE>
  487. Point p1 = new Point(10, 100);
  488. Group g = new Group(p1);
  489. p1.move(0, 0);
  490. System.out.println(Log.getString());
  491. </PRE></blockquote>
  492. <p> should print out "moving as a part of Group(Point(10, 100));",
  493. which you can do by using the toString() method already defined on
  494. Group. </p>
  495. <p> <em> Hint: This exercise combines the tracing from parts a and b.
  496. If you start with an aspect that includes the solutions to those
  497. previous exercises, you'll be most of the way there. </em> </p>
  498. <p> Test this with the JUnit test case <code>tests.Test3c</code>.
  499. <h3>Help Yourself by Helping Others</h3>
  500. <p> At this point, check the people to your left and right. If
  501. they're stuck somewhere, see if you can help them. </p>
  502. <!-- ============================== -->
  503. <hr />
  504. <!-- page break -->
  505. <h3>4. Caching</h3>
  506. <p> Computation of the bounding box of <code>Group</code> objects
  507. needs to deal with all aggregate parts of the group, and this
  508. computation can be expensive. In this section, we will explore
  509. various ways of reducing this expense. </p>
  510. <p> <strong>Optional</strong>: In all of these exercises, you should
  511. only deal with points that are added directly to Groups, rather than
  512. those that are added "indirectly" through Lines and Boxes. You should
  513. handle those points contained in Lines and Boxes only if time permits.
  514. </p>
  515. <h3>a. Make a constant override</h3>
  516. <p> <code>Group</code>'s <code>getBounds()</code> method could be
  517. understood to be a conservative approximation of the bounding box of a
  518. group. If that is true, then it would be a legal (and much faster)
  519. implementation of <code>getBounds()</code> to simply always return a
  520. rectangle consisting of the entire canvas, that is
  521. </p>
  522. <blockquote><PRE>
  523. new Rectangle(FigureElement.MIN_VALUE, FigureElement.MIN_VALUE,
  524. FigureElement.MAX_VALUE - FigureElement.MIN_VALUE,
  525. FigureElement.MAX_VALUE - FigureElement.MIN_VALUE)
  526. </PRE></blockquote>
  527. <p> Write an aspect to implement this change. You can override
  528. <code>Group</code>'s <code>getBounds()</code> method entirely with
  529. around advice intercepting the method.
  530. </p>
  531. <p> Your code should pass the JUnit test case
  532. <code>tests.Test4a</code> with this change.
  533. </p>
  534. <p> <strong>Answer: </strong>
  535. </p>
  536. <blockquote><PRE>
  537. package answers;
  538. import figures.*;
  539. import java.awt.Rectangle;
  540. aspect Answer4a {
  541. private Rectangle wholeCanvas =
  542. new Rectangle(FigureElement.MIN_VALUE, FigureElement.MIN_VALUE,
  543. FigureElement.MAX_VALUE - FigureElement.MIN_VALUE,
  544. FigureElement.MAX_VALUE - FigureElement.MIN_VALUE);
  545. Rectangle around(): execution(Rectangle Group.getBounds()) {
  546. return wholeCanvas;
  547. }
  548. }
  549. </PRE></blockquote>
  550. <h3>b. Make a constant cache</h3>
  551. <p> Instead of making the (very) conservative approximation of
  552. <code>getBounds()</code> from part (a), write an aspect instead that
  553. remembers the return value from the first time
  554. <code>getBounds()</code> has been called on a <code>Group</code>, and
  555. returns that first <code>Rectangle</code> for every subsequent
  556. call. </p>
  557. <p> <em>Hint: You can use an inter-type declaration to keep some
  558. state for every <code>Group</code> object.</em> </p>
  559. <p> Your code should pass the JUnit test case
  560. <code>tests.Test4b</code> with this change.
  561. </p>
  562. <h3>c. Invalidate, part 1</h3>
  563. <p> While caching in this way does save computation, it will lead to
  564. incorrect bounding boxes if a <code>Group</code> is ever moved.
  565. Change your aspect so that it invalidates the cache whenever the
  566. <code>move()</code> method of <code>Group</code> is called.
  567. </p>
  568. <p> Your code should pass the JUnit test case
  569. <code>tests.Test4c</code> with this change.
  570. </p>
  571. <h3>d. Invalidate, part 2</h3>
  572. <p> Of course, part (c) didn't really solve the problem. What if a
  573. <code>Point</code> that is part of a <code>Group</code> moves?
  574. Whenever either of a Point's fields are set it should invalidate the
  575. caches of all enclosing groups. Use your solution to problem 3c to
  576. modify your invalidation criteria in this way, but note that this is
  577. slightly different than the problem in 3c: Here you care about fields,
  578. where there you cared about method calls. </p>
  579. <p> Your code should pass the JUnit test case
  580. <code>tests.Test4d</code> with this change.
  581. </p>
  582. <h3>e. Invalidate, part 3</h3>
  583. <p> Did you really do part (d) correctly? Run the JUnit test
  584. <code>tests.Test4e</code> to see. If you pass, congratulations, now
  585. go help other people. Otherwise, you have fallen prey to our cruel
  586. trap: Remember that whenever a point moves it should invalidate the
  587. caches of <em>all</em> enclosing groups. </p>
  588. <hr>
  589. </body> </html>