1 | ### Eclipse Workspace Patch 1.0 |
---|
2 | #P sophie2-platform |
---|
3 | Index: modules/org.sophie2.core/src/test/java/org/sophie2/core/prolib/list/ValueProListTest.java |
---|
4 | =================================================================== |
---|
5 | --- modules/org.sophie2.core/src/test/java/org/sophie2/core/prolib/list/ValueProListTest.java (revision 2425) |
---|
6 | +++ modules/org.sophie2.core/src/test/java/org/sophie2/core/prolib/list/ValueProListTest.java (working copy) |
---|
7 | @@ -167,9 +167,9 @@ |
---|
8 | assertEquals(0, this.l1.size()); |
---|
9 | List<Pro> reads = Registry.get().endReadTrack(this); |
---|
10 | assertEquals(1, reads.size()); |
---|
11 | - assertSame(this.l1, reads.get(0)); |
---|
12 | + assertSame(this.l1.getRwCache().getSizeAspect(), reads.get(0)); |
---|
13 | |
---|
14 | - //no changes |
---|
15 | + // no changes |
---|
16 | assertEquals(Arrays.asList(), this.l1c); |
---|
17 | assertEquals(Arrays.asList(), this.dpc); |
---|
18 | |
---|
19 | @@ -218,7 +218,7 @@ |
---|
20 | assertEquals(""+reads, 1, reads.size()); //it actually reads, add depends on whats inside... |
---|
21 | |
---|
22 | assertEquals(1, reads.size()); |
---|
23 | - assertSame(this.l1, reads.get(0)); |
---|
24 | + assertSame(this.l1.getRwCache().getSizeAspect(), reads.get(0)); |
---|
25 | assertEquals(1, this.l1c.size()); |
---|
26 | assertEquals(0, this.dpc.size()); |
---|
27 | BaseProList<String>.BaseProListChange c0 = autoCast(this.l1c.get(0)); |
---|
28 | @@ -230,7 +230,7 @@ |
---|
29 | |
---|
30 | assertTrue(this.l1.add("b1")); |
---|
31 | |
---|
32 | - assertSame(this.l1, reads.get(0)); |
---|
33 | + assertSame(this.l1.getRwCache().getSizeAspect(), reads.get(0)); |
---|
34 | assertEquals(2, this.l1c.size()); |
---|
35 | assertEquals(0, this.dpc.size()); |
---|
36 | BaseProList<String>.BaseProListChange c1 = autoCast(this.l1c.get(1)); |
---|
37 | Index: modules/org.sophie2.core/src/test/java/org/sophie2/core/prolib/list/BaseProListAspectsTest.java |
---|
38 | =================================================================== |
---|
39 | --- modules/org.sophie2.core/src/test/java/org/sophie2/core/prolib/list/BaseProListAspectsTest.java (revision 0) |
---|
40 | +++ modules/org.sophie2.core/src/test/java/org/sophie2/core/prolib/list/BaseProListAspectsTest.java (revision 0) |
---|
41 | @@ -0,0 +1,381 @@ |
---|
42 | +package org.sophie2.core.prolib.list; |
---|
43 | + |
---|
44 | +import java.awt.Color; |
---|
45 | +import java.util.ArrayList; |
---|
46 | +import java.util.Arrays; |
---|
47 | +import java.util.Collection; |
---|
48 | +import java.util.List; |
---|
49 | +import java.util.Map; |
---|
50 | + |
---|
51 | +import org.junit.Test; |
---|
52 | +import org.sophie2.core.prolib.Aspect; |
---|
53 | +import org.sophie2.core.prolib.ProStatus; |
---|
54 | +import org.sophie2.core.prolib.impl.AutoProperty; |
---|
55 | +import org.sophie2.core.prolib.impl.BaseProObject; |
---|
56 | +import org.sophie2.core.prolib.interfaces.Pro; |
---|
57 | +import org.sophie2.core.prolib.interfaces.Prop; |
---|
58 | +import org.sophie2.core.prolib.interfaces.RwListProp; |
---|
59 | +import org.sophie2.core.prolib.util.ChangeManager; |
---|
60 | +import org.sophie2.core.prolib.util.DefaultChangeManager; |
---|
61 | +import org.sophie2.core.prolib.util.Registry; |
---|
62 | +import org.sophie2.core.testing.UnitTestBase; |
---|
63 | + |
---|
64 | +/** |
---|
65 | + * El mucho grande test for {@link Aspect}s usage with {@link BaseProList}s. |
---|
66 | + * Test is meant to work with the size {@link Aspect} enabled and it does! |
---|
67 | + * Currently size {@link Aspect} is disabled so this test fails. |
---|
68 | + * Will fix when size {@link Aspect} is fixed. --gogov 2009-05-08 8:56 PM |
---|
69 | + * |
---|
70 | + * @author gogov |
---|
71 | + */ |
---|
72 | +@SuppressWarnings("all") |
---|
73 | +public class BaseProListAspectsTest extends UnitTestBase { |
---|
74 | + class TestProList<T> extends BaseProList<T> { |
---|
75 | + public TestProList() { |
---|
76 | + this.new StatusChange(ProStatus.READY).register(); |
---|
77 | + } |
---|
78 | + |
---|
79 | + public Aspect getSizeAspect() { |
---|
80 | + return getRwCache().getSizeAspect(); |
---|
81 | + } |
---|
82 | + |
---|
83 | + public Map<Object, Aspect> getKeyAspectsMap() { |
---|
84 | + return getMultimap().getAspectsMap(); |
---|
85 | + } |
---|
86 | + |
---|
87 | + public List<Aspect> getKeyAspects() { |
---|
88 | + return new ArrayList<Aspect>(getKeyAspectsMap().values()); |
---|
89 | + } |
---|
90 | + |
---|
91 | + public List<Aspect> getAllAspects() { |
---|
92 | + List<Aspect> res = getKeyAspects(); |
---|
93 | + res.add(0, getSizeAspect()); |
---|
94 | + |
---|
95 | + return res; |
---|
96 | + } |
---|
97 | + |
---|
98 | + @Override |
---|
99 | + protected List<T> getPublicCache() { |
---|
100 | + return getRwCache(); |
---|
101 | + } |
---|
102 | + |
---|
103 | + @Override |
---|
104 | + public String fullId() { |
---|
105 | + return "TestProList"; |
---|
106 | + } |
---|
107 | + |
---|
108 | + @Override |
---|
109 | + public ChangeManager locateChangeManager() { |
---|
110 | + return DefaultChangeManager.get(); |
---|
111 | + } |
---|
112 | + |
---|
113 | + } |
---|
114 | + |
---|
115 | + abstract class ReadsHelper { |
---|
116 | + public List<Pro> run() { |
---|
117 | + try { |
---|
118 | + Registry.get().beginReadTrack(this); |
---|
119 | + method(); |
---|
120 | + } |
---|
121 | + finally { |
---|
122 | + return Registry.get().endReadTrack(this); |
---|
123 | + } |
---|
124 | + } |
---|
125 | + |
---|
126 | + public List<Pro> run(int expectedReadsCount) { |
---|
127 | + try { |
---|
128 | + Registry.get().beginReadTrack(this); |
---|
129 | + method(); |
---|
130 | + } |
---|
131 | + finally { |
---|
132 | + List<Pro> reads = Registry.get().endReadTrack(this); |
---|
133 | + assertEquals(expectedReadsCount, reads.size()); |
---|
134 | + |
---|
135 | + return reads; |
---|
136 | + } |
---|
137 | + } |
---|
138 | + |
---|
139 | + public List<Pro> run(List<Pro> expectedReadPros) { |
---|
140 | + try { |
---|
141 | + Registry.get().beginReadTrack(this); |
---|
142 | + method(); |
---|
143 | + } |
---|
144 | + finally { |
---|
145 | + List<Pro> reads = Registry.get().endReadTrack(this); |
---|
146 | + assertSameContent(expectedReadPros, reads); |
---|
147 | + |
---|
148 | + return reads; |
---|
149 | + } |
---|
150 | + } |
---|
151 | + |
---|
152 | + public List<Pro> run(Pro ... expectedReadsPro) { |
---|
153 | + return run(Arrays.asList(expectedReadsPro)); |
---|
154 | + } |
---|
155 | + |
---|
156 | + |
---|
157 | + abstract protected void method(); |
---|
158 | + } |
---|
159 | + |
---|
160 | + private TestProList beers = new TestProList<String>(); |
---|
161 | + |
---|
162 | + @Test |
---|
163 | + public void testCacheListAspectsReads() { |
---|
164 | + new ReadsHelper() { |
---|
165 | + @Override |
---|
166 | + protected void method() { |
---|
167 | + beers.add("zagorka"); // read of size aspect |
---|
168 | + beers.add("kamenitza"); // read of size aspect |
---|
169 | + beers.add("pirinsko"); // read of size aspect |
---|
170 | + beers.add("shumensko"); // read of size aspect |
---|
171 | + } |
---|
172 | + }.run(beers.getSizeAspect()); |
---|
173 | + |
---|
174 | + new ReadsHelper() { |
---|
175 | + @Override |
---|
176 | + protected void method() { |
---|
177 | + beers.set(0, "stolichno"); // read of root aspect |
---|
178 | + } |
---|
179 | + }.run((Pro)beers); |
---|
180 | + |
---|
181 | + new ReadsHelper() { |
---|
182 | + @Override |
---|
183 | + protected void method() { |
---|
184 | + beers.remove(0); // read of root aspect |
---|
185 | + } |
---|
186 | + }.run((Pro)beers); |
---|
187 | + |
---|
188 | + new ReadsHelper() { |
---|
189 | + @Override |
---|
190 | + protected void method() { |
---|
191 | + beers.size(); // read of size aspect |
---|
192 | + } |
---|
193 | + }.run(beers.getSizeAspect()); |
---|
194 | + |
---|
195 | + new ReadsHelper() { |
---|
196 | + @Override |
---|
197 | + protected void method() { |
---|
198 | + beers.get(0); // read of root aspect |
---|
199 | + } |
---|
200 | + }.run((Pro)beers); |
---|
201 | + } |
---|
202 | + |
---|
203 | + @Test |
---|
204 | + public void testKeyAspectsReads() { |
---|
205 | + beers.add("zagorka"); |
---|
206 | + beers.add("zagorka"); |
---|
207 | + beers.add("kamenitza"); |
---|
208 | + beers.add("pirinsko"); |
---|
209 | + beers.add("shumensko"); |
---|
210 | + beers.set(0, "stolichno"); |
---|
211 | + beers.remove(0); |
---|
212 | + beers.size(); |
---|
213 | + |
---|
214 | + List<Pro> reads = |
---|
215 | + new ReadsHelper() { |
---|
216 | + @Override |
---|
217 | + protected void method() { |
---|
218 | + beers.findAll(""); |
---|
219 | + beers.findAll("zagorka"); |
---|
220 | + beers.findAll("meddle"); |
---|
221 | + beers.findOne("meddle"); |
---|
222 | + beers.findAll("meddle"); |
---|
223 | + beers.findOne("non-existing-key"); |
---|
224 | + beers.findOne("non-existing-key"); |
---|
225 | + beers.findOne("non-existing-key"); |
---|
226 | + } |
---|
227 | + }.run(); |
---|
228 | + |
---|
229 | + assertSameContent( |
---|
230 | + new ArrayList(values(beers.getKeyAspectsMap(), |
---|
231 | + "", "zagorka", "meddle", "non-existing-key")), |
---|
232 | + reads); |
---|
233 | + } |
---|
234 | + |
---|
235 | + class JunkProObject extends BaseProObject { |
---|
236 | + public RwListProp<Object> records() { |
---|
237 | + return getBean().makeListProp("records", Object.class); |
---|
238 | + } |
---|
239 | + } |
---|
240 | + |
---|
241 | + final JunkProObject junkie = new JunkProObject(); |
---|
242 | + |
---|
243 | + |
---|
244 | + abstract class AutoHelper extends BaseProObject { |
---|
245 | + private int calls; |
---|
246 | + |
---|
247 | + public AutoHelper() { |
---|
248 | + this.calls = 0; |
---|
249 | + } |
---|
250 | + |
---|
251 | + public int getCalls() { |
---|
252 | + return this.calls; |
---|
253 | + } |
---|
254 | + |
---|
255 | + public Prop<Integer> prop() { |
---|
256 | + class prop extends AutoProperty<Integer> { |
---|
257 | + protected Integer compute() { |
---|
258 | + ++AutoHelper.this.calls; |
---|
259 | + method(); |
---|
260 | + return AutoHelper.this.calls; |
---|
261 | + } |
---|
262 | + } |
---|
263 | + return getBean().makeProp(prop.class); |
---|
264 | + } |
---|
265 | + |
---|
266 | + abstract protected void method(); |
---|
267 | + } |
---|
268 | + |
---|
269 | + @Test |
---|
270 | + public void testAutoPropertyCalls() { |
---|
271 | + AutoHelper h0 = new AutoHelper() { |
---|
272 | + @Override |
---|
273 | + protected void method() { |
---|
274 | + // nothing |
---|
275 | + } |
---|
276 | + }; |
---|
277 | + |
---|
278 | + AutoHelper h1 = new AutoHelper() { |
---|
279 | + @Override |
---|
280 | + protected void method() { |
---|
281 | + junkie.records().get().size(); |
---|
282 | + } |
---|
283 | + }; |
---|
284 | + |
---|
285 | + AutoHelper h2 = new AutoHelper() { |
---|
286 | + @Override |
---|
287 | + protected void method() { |
---|
288 | + junkie.records().get().findAll("person"); |
---|
289 | + } |
---|
290 | + }; |
---|
291 | + |
---|
292 | + AutoHelper h3 = new AutoHelper() { |
---|
293 | + @Override |
---|
294 | + protected void method() { |
---|
295 | + junkie.records().get().findAll("beer"); |
---|
296 | + } |
---|
297 | + }; |
---|
298 | + |
---|
299 | + AutoHelper h4 = new AutoHelper() { |
---|
300 | + @Override |
---|
301 | + protected void method() { |
---|
302 | + junkie.records().get().findAll(null); |
---|
303 | + } |
---|
304 | + }; |
---|
305 | + |
---|
306 | + AutoHelper h5 = new AutoHelper() { |
---|
307 | + @Override |
---|
308 | + protected void method() { |
---|
309 | + junkie.records().get().findAll(new Integer(6)); |
---|
310 | + } |
---|
311 | + }; |
---|
312 | + |
---|
313 | + AutoHelper h6 = new AutoHelper() { |
---|
314 | + @Override |
---|
315 | + protected void method() { |
---|
316 | + for(Object obj : junkie.records().get()) { |
---|
317 | + // nothing |
---|
318 | + } |
---|
319 | + } |
---|
320 | + }; |
---|
321 | + |
---|
322 | + AutoHelper h7 = new AutoHelper() { |
---|
323 | + @Override |
---|
324 | + protected void method() { |
---|
325 | + junkie.records().get().size(); |
---|
326 | + junkie.records().get().findAll("beer"); |
---|
327 | + } |
---|
328 | + }; |
---|
329 | + |
---|
330 | + AutoHelper h8 = new AutoHelper() { |
---|
331 | + @Override |
---|
332 | + protected void method() { |
---|
333 | + junkie.records().get().findAll("junk-key1"); |
---|
334 | + junkie.records().get().findAll("junk-key2"); |
---|
335 | + junkie.records().get().findAll("junk-key3"); |
---|
336 | + junkie.records().get().findAll("junk-key4"); |
---|
337 | + junkie.records().get().findAll("junk-key5"); |
---|
338 | + junkie.records().get().findAll("junk-key6"); |
---|
339 | + } |
---|
340 | + }; |
---|
341 | + |
---|
342 | + AutoHelper h9 = new AutoHelper() { |
---|
343 | + @Override |
---|
344 | + protected void method() { |
---|
345 | + junkie.records().get().size(); |
---|
346 | + junkie.records().get().findAll("beer"); |
---|
347 | + junkie.records().get().findOne(new Integer(6)); |
---|
348 | + junkie.records().get().findAll("never-added"); |
---|
349 | + junkie.records().get().findAll("meddle"); |
---|
350 | + } |
---|
351 | + }; |
---|
352 | + |
---|
353 | + h0.prop().get(); |
---|
354 | + h1.prop().get(); |
---|
355 | + h2.prop().get(); |
---|
356 | + h3.prop().get(); |
---|
357 | + h4.prop().get(); |
---|
358 | + h5.prop().get(); |
---|
359 | + h6.prop().get(); |
---|
360 | + h7.prop().get(); |
---|
361 | + h8.prop().get(); |
---|
362 | + h9.prop().get(); |
---|
363 | + |
---|
364 | + junkie.records().add(new ListEntry("person", "meddle")); |
---|
365 | + junkie.records().add(new ListEntry("person", "pique")); |
---|
366 | + junkie.records().add(new ListEntry("person", "drogba")); |
---|
367 | + junkie.records().add(new ListEntry("person", "gay-referee")); |
---|
368 | + junkie.records().add(new ListEntry("beer", "zagorka")); |
---|
369 | + junkie.records().add(Integer.valueOf(6)); |
---|
370 | + junkie.records().add("meddle"); |
---|
371 | + junkie.records().add(null); |
---|
372 | + junkie.records().add(new ListEntry("junk-key1", 5)); |
---|
373 | + junkie.records().add(new ListEntry("junk-key2", new Object())); |
---|
374 | + junkie.records().add(new ListEntry("junk-key3", "jeha")); |
---|
375 | + junkie.records().add(new ListEntry("junk-key4", Color.black)); |
---|
376 | + junkie.records().add(new ListEntry("junk-key5", 'a')); |
---|
377 | + junkie.records().add(new ListEntry("junk-key6", 123.543)); |
---|
378 | + junkie.records().remove(3); |
---|
379 | + junkie.records().remove(5); |
---|
380 | + junkie.records().remove(1); |
---|
381 | + junkie.records().remove(0); |
---|
382 | + junkie.records().remove(0); |
---|
383 | + junkie.records().set(0, "meddle"); |
---|
384 | + junkie.records().set(2, new ListEntry("person", "o'sullivan")); |
---|
385 | + junkie.records().set(3, "meddle"); |
---|
386 | + junkie.records().set(2, new ListEntry("beer", "stolichno")); |
---|
387 | + junkie.records().set(5, "meddle"); |
---|
388 | + |
---|
389 | + h0.prop().get(); |
---|
390 | + h1.prop().get(); |
---|
391 | + h2.prop().get(); |
---|
392 | + h3.prop().get(); |
---|
393 | + h4.prop().get(); |
---|
394 | + h5.prop().get(); |
---|
395 | + h6.prop().get(); |
---|
396 | + h7.prop().get(); |
---|
397 | + h8.prop().get(); |
---|
398 | + h9.prop().get(); |
---|
399 | + |
---|
400 | + assertEquals(1, h0.getCalls()); |
---|
401 | + assertEquals(20, h1.getCalls()); |
---|
402 | + assertEquals(11, h2.getCalls()); |
---|
403 | + assertEquals(4, h3.getCalls()); |
---|
404 | + assertEquals(3, h4.getCalls()); |
---|
405 | + assertEquals(2, h5.getCalls()); |
---|
406 | + assertEquals(25, h6.getCalls()); |
---|
407 | + |
---|
408 | + assertEquals(23, h7.getCalls()); |
---|
409 | + assertEquals(9, h8.getCalls()); |
---|
410 | + assertEquals(29, h9.getCalls()); |
---|
411 | + } |
---|
412 | + |
---|
413 | + public static <K, V> Collection<V> values(Map<K, V> map, K ... keys) { |
---|
414 | + Collection<V> c = new ArrayList<V>(); |
---|
415 | + |
---|
416 | + for(K key : keys) { |
---|
417 | + c.add(map.get(key)); |
---|
418 | + } |
---|
419 | + |
---|
420 | + return c; |
---|
421 | + } |
---|
422 | +} |
---|
423 | Index: modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/BasePro.java |
---|
424 | =================================================================== |
---|
425 | --- modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/BasePro.java (revision 2364) |
---|
426 | +++ modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/BasePro.java (working copy) |
---|
427 | @@ -49,7 +49,6 @@ |
---|
428 | * |
---|
429 | */ |
---|
430 | protected void registerRead() { |
---|
431 | - |
---|
432 | Registry.get().registerRead(this); |
---|
433 | |
---|
434 | if(getLastState() == ProStatus.CREATED) { |
---|
435 | @@ -119,11 +118,11 @@ |
---|
436 | * @param change |
---|
437 | * The change to be sent. |
---|
438 | */ |
---|
439 | - private void fireChanged(ProChange change) { |
---|
440 | + protected void fireChanged(ProChange change) { |
---|
441 | assert change.getSource() == this; |
---|
442 | Object key = new Object(); |
---|
443 | try { |
---|
444 | - // ignore all reades caused by changes |
---|
445 | + // ignore all reads caused by changes |
---|
446 | Registry.get().beginReadTrack(key); |
---|
447 | changed(change); |
---|
448 | List<ProListener> list = new ArrayList<ProListener>( |
---|
449 | @@ -166,7 +165,7 @@ |
---|
450 | public BaseProChange(BasePro source) { |
---|
451 | super(source); |
---|
452 | |
---|
453 | - // and check whether hasEffect is overriden in the bottom level |
---|
454 | + // and check whether hasEffect is overridden in the bottom level |
---|
455 | // class |
---|
456 | try { |
---|
457 | getClass().getDeclaredMethod("hasEffect"); |
---|
458 | @@ -183,11 +182,9 @@ |
---|
459 | super(BasePro.this); |
---|
460 | } |
---|
461 | |
---|
462 | - @SuppressWarnings("synthetic-access") |
---|
463 | @Override |
---|
464 | protected void fire() { |
---|
465 | getSource().fireChanged(this); |
---|
466 | - |
---|
467 | } |
---|
468 | |
---|
469 | @Override |
---|
470 | @@ -220,7 +217,7 @@ |
---|
471 | } |
---|
472 | |
---|
473 | /** |
---|
474 | - * A change of the state of a BasePro. Implementors, dont forget ot override |
---|
475 | + * A change of the state of a BasePro. Implementors, don't forget to override |
---|
476 | * {@link #hasEffect()}! |
---|
477 | * |
---|
478 | * @author milo |
---|
479 | @@ -244,15 +241,15 @@ |
---|
480 | public StatusChange(BasePro source, ProStatus oldStatus, |
---|
481 | ProStatus newStatus) { |
---|
482 | super(source); |
---|
483 | - assert newStatus.isMetaAvailable(); //can not revert |
---|
484 | - //to crreated |
---|
485 | + assert newStatus.isMetaAvailable(); // can not revert |
---|
486 | + // to created |
---|
487 | this.oldStatus = oldStatus; |
---|
488 | this.newStatus = newStatus; |
---|
489 | |
---|
490 | } |
---|
491 | |
---|
492 | /** |
---|
493 | - * Simpler constructor. Uses the state of the eclosing class for |
---|
494 | + * Simpler constructor. Uses the state of the enclosing class for |
---|
495 | * oldStatus. |
---|
496 | * |
---|
497 | * @param newStatus |
---|
498 | Index: modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/impl/ObjectProperty.java |
---|
499 | =================================================================== |
---|
500 | --- modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/impl/ObjectProperty.java (revision 2425) |
---|
501 | +++ modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/impl/ObjectProperty.java (working copy) |
---|
502 | @@ -292,7 +292,7 @@ |
---|
503 | * Gets the last (on last update) value or null if never computed. If the |
---|
504 | * property is in {@link ProStatus#READY} state it will do the same as |
---|
505 | * {@link #get()} However, this method does not cause a read to be |
---|
506 | - * registered, so it is usefull for assertions. |
---|
507 | + * registered, so it is useful for assertions. |
---|
508 | * |
---|
509 | * @return The last computed value. |
---|
510 | */ |
---|
511 | Index: modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/util/ReflectionUtil.java |
---|
512 | =================================================================== |
---|
513 | --- modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/util/ReflectionUtil.java (revision 2425) |
---|
514 | +++ modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/util/ReflectionUtil.java (working copy) |
---|
515 | @@ -8,7 +8,8 @@ |
---|
516 | import java.lang.reflect.ParameterizedType; |
---|
517 | import java.lang.reflect.Type; |
---|
518 | import java.lang.reflect.TypeVariable; |
---|
519 | - |
---|
520 | +import java.util.HashMap; |
---|
521 | +import java.util.Map; |
---|
522 | |
---|
523 | import org.apache.log4j.Logger; |
---|
524 | import org.sophie2.core.prolib.annot.NoProp; |
---|
525 | @@ -74,6 +75,56 @@ |
---|
526 | } |
---|
527 | return findMethodAnnotation(m, c.getSuperclass(), annotationClass); |
---|
528 | } |
---|
529 | + |
---|
530 | + |
---|
531 | + private static class AnnoQuery { |
---|
532 | + private final Method method; |
---|
533 | + private final Class<?> annoClass; |
---|
534 | + public AnnoQuery(Method method, Class<?> annoClass) { |
---|
535 | + super(); |
---|
536 | + this.method = method; |
---|
537 | + this.annoClass = annoClass; |
---|
538 | + } |
---|
539 | + public Method getMethod() { |
---|
540 | + return this.method; |
---|
541 | + } |
---|
542 | + public Class<?> getAnnoClass() { |
---|
543 | + return this.annoClass; |
---|
544 | + } |
---|
545 | + @Override |
---|
546 | + public int hashCode() { |
---|
547 | + final int prime = 31; |
---|
548 | + int result = 1; |
---|
549 | + result = prime * result |
---|
550 | + + ((this.annoClass == null) ? 0 : this.annoClass.hashCode()); |
---|
551 | + result = prime * result |
---|
552 | + + ((this.method == null) ? 0 : this.method.hashCode()); |
---|
553 | + return result; |
---|
554 | + } |
---|
555 | + @Override |
---|
556 | + public boolean equals(Object obj) { |
---|
557 | + if (this == obj) |
---|
558 | + return true; |
---|
559 | + if (obj == null) |
---|
560 | + return false; |
---|
561 | + if (getClass() != obj.getClass()) |
---|
562 | + return false; |
---|
563 | + AnnoQuery other = (AnnoQuery) obj; |
---|
564 | + if (this.annoClass == null) { |
---|
565 | + if (other.annoClass != null) |
---|
566 | + return false; |
---|
567 | + } else if (!this.annoClass.equals(other.annoClass)) |
---|
568 | + return false; |
---|
569 | + if (this.method == null) { |
---|
570 | + if (other.method != null) |
---|
571 | + return false; |
---|
572 | + } else if (!this.method.equals(other.method)) |
---|
573 | + return false; |
---|
574 | + return true; |
---|
575 | + } |
---|
576 | + |
---|
577 | + } |
---|
578 | + private static Map<AnnoQuery, Object> annoCache = new HashMap<AnnoQuery, Object>(); |
---|
579 | |
---|
580 | /** |
---|
581 | * Finds annotation for a method traversing its hierarchy up. If the annotation |
---|
582 | @@ -84,9 +135,15 @@ |
---|
583 | * @param annotationClass the annotation class. |
---|
584 | * @return the found annotation, or null. |
---|
585 | */ |
---|
586 | + @SuppressWarnings("unchecked") |
---|
587 | public static <T extends Annotation> T findMethodAnnotation(Method m, |
---|
588 | Class<T> annotationClass) { |
---|
589 | - return findMethodAnnotation(m, m.getDeclaringClass(), annotationClass); |
---|
590 | + AnnoQuery q = new AnnoQuery(m, annotationClass); |
---|
591 | + if(!annoCache.containsKey(q)) { |
---|
592 | + T res = findMethodAnnotation(m, m.getDeclaringClass(), annotationClass); |
---|
593 | + annoCache.put(q, res); |
---|
594 | + } |
---|
595 | + return (T)annoCache.get(q); |
---|
596 | } |
---|
597 | |
---|
598 | /** |
---|
599 | Index: modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/list/ProList.java |
---|
600 | =================================================================== |
---|
601 | --- modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/list/ProList.java (revision 2425) |
---|
602 | +++ modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/list/ProList.java (working copy) |
---|
603 | @@ -99,27 +99,26 @@ |
---|
604 | * The key is: |
---|
605 | * * the key of the {@link ListEntry} if {@link ListEntry}s were previously added to the list. |
---|
606 | * * an {@link Immutable} object if immutables were previously added. |
---|
607 | - * * null in all other cases. Note that if nulls, simple Objects or ProObject are added, this method will |
---|
608 | + * * null in all other cases. Note that if nulls, simple Objects or ProObject are added, and this method will |
---|
609 | * return a {@link List} of all these. That is why it is not recommended to use this method if the list contains |
---|
610 | * nulls, Objects or ProObjects. |
---|
611 | * Although the the method is supposed to behave properly if the list contains different value types, |
---|
612 | * one is not encouraged to use the method in such cases since it may lead to misunderstandings. |
---|
613 | * |
---|
614 | - * |
---|
615 | * @param key |
---|
616 | * The key to search for. |
---|
617 | * @return |
---|
618 | * {@link List} of elements whose key is the specified key. The {@link List} will be empty if no such |
---|
619 | * key is previously added. |
---|
620 | */ |
---|
621 | - public List<ListEntry> findAll (Object key); |
---|
622 | + public List<E> findAll (Object key); |
---|
623 | |
---|
624 | /** |
---|
625 | * Finds all elements that have the provided key. |
---|
626 | - * The key is: |
---|
627 | + * The key, by convention, is: |
---|
628 | * * the key of the {@link ListEntry} if {@link ListEntry}s were previously added to the list. |
---|
629 | * * an {@link Immutable} object if immutables were previously added. |
---|
630 | - * * null in all other cases. Note that if nulls, simple Objects or ProObject are added, this method will |
---|
631 | + * * null in all other cases. Note that if nulls, simple Objects or ProObject are added, and this method will |
---|
632 | * return a {@link List} of all these. That is why it is not recommended to use this method if the list contains |
---|
633 | * nulls, Objects or ProObjects. |
---|
634 | * Throws {@link RuntimeException} if more that one elements are contained with |
---|
635 | @@ -128,9 +127,9 @@ |
---|
636 | * one is not encouraged to use the method in such cases since it may lead to misunderstandings. |
---|
637 | * |
---|
638 | * @param key |
---|
639 | - * The key to find {@link ListEntry} for. |
---|
640 | + * The key to search for. |
---|
641 | * @return |
---|
642 | - * The {@link ListEntry} found or null if it does not exist. |
---|
643 | + * The element found or null if it does not exist. |
---|
644 | */ |
---|
645 | - public ListEntry findOne (Object key); |
---|
646 | + public E findOne (Object key); |
---|
647 | } |
---|
648 | \ No newline at end of file |
---|
649 | Index: modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/Aspect.java |
---|
650 | =================================================================== |
---|
651 | --- modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/Aspect.java (revision 0) |
---|
652 | +++ modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/Aspect.java (revision 0) |
---|
653 | @@ -0,0 +1,93 @@ |
---|
654 | +package org.sophie2.core.prolib; |
---|
655 | + |
---|
656 | +import java.util.ArrayList; |
---|
657 | +import java.util.List; |
---|
658 | + |
---|
659 | +import org.sophie2.core.prolib.errors.NotAvailableException; |
---|
660 | +import org.sophie2.core.prolib.events.ProChange; |
---|
661 | +import org.sophie2.core.prolib.events.ProListener; |
---|
662 | +import org.sophie2.core.prolib.list.BaseProList; |
---|
663 | +import org.sophie2.core.prolib.util.ChangeManager; |
---|
664 | +import org.sophie2.core.prolib.util.DefaultChangeManager; |
---|
665 | +import org.sophie2.core.prolib.util.Registry; |
---|
666 | + |
---|
667 | +/** |
---|
668 | + * A class representing a given aspect of a given {@link BasePro}. |
---|
669 | + * Currently used only with {@link BaseProList}s. |
---|
670 | + * |
---|
671 | + * {@link Aspect}s can be used by {@link BasePro}s internally to reduce the attached listeners |
---|
672 | + * and the registered reads. |
---|
673 | + * |
---|
674 | + * This way: |
---|
675 | + * 1) Some reads could be registered not with the {@link BasePro} itself but with an {@link Aspect} |
---|
676 | + * of the {@link BasePro}, and so potentially lots of listeners will be attached to the |
---|
677 | + * {@link Aspect}s, instead of the {@link BasePro}. |
---|
678 | + * 2) ProChanges will be fired from the {@link Aspect}s of the {@link BasePro} to registered |
---|
679 | + * listeners, instead of always firing from the {@link BasePro} itself. |
---|
680 | + * |
---|
681 | + * @author gogov |
---|
682 | + */ |
---|
683 | +public class Aspect extends BasePro { |
---|
684 | + private final BasePro owner; |
---|
685 | + |
---|
686 | + /** |
---|
687 | + * Creates an {@link Aspect} with a given owner. |
---|
688 | + * @param owner |
---|
689 | + * The owner this {@link Aspect} belongs to. |
---|
690 | + */ |
---|
691 | + public Aspect(BasePro owner) { |
---|
692 | + this.owner = owner; |
---|
693 | + } |
---|
694 | + |
---|
695 | + @Override |
---|
696 | + public String fullId() { |
---|
697 | + return "Aspect"; |
---|
698 | + } |
---|
699 | + |
---|
700 | + @Override |
---|
701 | + public ChangeManager locateChangeManager() { |
---|
702 | + return DefaultChangeManager.get(); |
---|
703 | + } |
---|
704 | + |
---|
705 | + /** |
---|
706 | + * Returns the owner of this {@link Aspect}. |
---|
707 | + * @return |
---|
708 | + * The owner of this {@link Aspect}. |
---|
709 | + */ |
---|
710 | + public BasePro getOwner() { |
---|
711 | + return this.owner; |
---|
712 | + } |
---|
713 | + |
---|
714 | + @Override |
---|
715 | + public void registerRead() { |
---|
716 | + Registry.get().registerRead(this); |
---|
717 | + |
---|
718 | + if(getOwner().getLastState() == ProStatus.CREATED) { |
---|
719 | + throw new NotAvailableException(fullId() + " attributes not initialized"); |
---|
720 | + } |
---|
721 | + else if(getOwner().getLastState() == ProStatus.DESTROYED) { |
---|
722 | + throw new NotAvailableException(fullId() + " already destroyed!"); |
---|
723 | + } |
---|
724 | + else if(getOwner().getLastState() == ProStatus.META_INITIALIZED) { |
---|
725 | + throw new NotAvailableException(fullId() + " value not initialized!", lastProblem()); |
---|
726 | + } |
---|
727 | + } |
---|
728 | + |
---|
729 | + @Override |
---|
730 | + public void fireChanged(ProChange change) { |
---|
731 | + assert change.getSource() == getOwner(); |
---|
732 | + Object key = new Object(); |
---|
733 | + try { |
---|
734 | + // ignore all reads caused by changes |
---|
735 | + Registry.get().beginReadTrack(key); |
---|
736 | + changed(change); |
---|
737 | + List<ProListener> list = new ArrayList<ProListener>( |
---|
738 | + getListeners()); |
---|
739 | + for (ProListener l : list) { |
---|
740 | + l.changed(change); |
---|
741 | + } |
---|
742 | + } finally { |
---|
743 | + Registry.get().endReadTrack(key); |
---|
744 | + } |
---|
745 | + } |
---|
746 | +} |
---|
747 | Index: modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/impl/AutoTracker.java |
---|
748 | =================================================================== |
---|
749 | --- modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/impl/AutoTracker.java (revision 2425) |
---|
750 | +++ modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/impl/AutoTracker.java (working copy) |
---|
751 | @@ -135,7 +135,7 @@ |
---|
752 | } |
---|
753 | |
---|
754 | /** |
---|
755 | - * Override this method to provide a name that can distinguis this tracked. |
---|
756 | + * Override this method to provide a name that can distinguish this tracked. |
---|
757 | * It helps debugging. |
---|
758 | * @return a string identifier. |
---|
759 | */ |
---|
760 | Index: modules/org.sophie2.core/src/test/java/org/sophie2/core/prolib/list/BaseProListIndexingTest.java |
---|
761 | =================================================================== |
---|
762 | --- modules/org.sophie2.core/src/test/java/org/sophie2/core/prolib/list/BaseProListIndexingTest.java (revision 2425) |
---|
763 | +++ modules/org.sophie2.core/src/test/java/org/sophie2/core/prolib/list/BaseProListIndexingTest.java (working copy) |
---|
764 | @@ -263,9 +263,9 @@ |
---|
765 | } |
---|
766 | |
---|
767 | this.immDummyList.remove(new ImmDummy(10)); |
---|
768 | - ListEntry immDummyEntry = this.immDummyList.findOne(new ImmDummy(10)); |
---|
769 | - assertEquals(immDummy, immDummyEntry.getValue()); |
---|
770 | - assertSame(immDummy, immDummyEntry.getValue()); |
---|
771 | + ImmDummy immDummyFound = this.immDummyList.findOne(new ImmDummy(10)); |
---|
772 | + assertEquals(immDummy, immDummyFound); |
---|
773 | + assertSame(immDummy, immDummyFound); |
---|
774 | |
---|
775 | Dummy dummy1 = new Dummy(3); |
---|
776 | Dummy dummy2 = new Dummy(4); |
---|
777 | @@ -282,9 +282,9 @@ |
---|
778 | |
---|
779 | this.dummyList.remove(dummy2); |
---|
780 | |
---|
781 | - ListEntry dummyEntry = this.dummyList.findOne(null); |
---|
782 | - assertSame(dummy1, dummyEntry.getValue()); |
---|
783 | - assertEquals(dummy1, dummyEntry.getValue()); |
---|
784 | + Dummy dummyFound = this.dummyList.findOne(null); |
---|
785 | + assertSame(dummy1, dummyFound); |
---|
786 | + assertEquals(dummy1, dummyFound); |
---|
787 | |
---|
788 | this.entryList.add(new ListEntry("sophie2", 123)); |
---|
789 | |
---|
790 | @@ -315,11 +315,11 @@ |
---|
791 | |
---|
792 | this.objectList.remove(1); |
---|
793 | |
---|
794 | - assertEquals(o1, this.objectList.findOne(null).getValue()); |
---|
795 | + assertEquals(o1, this.objectList.findOne(null)); |
---|
796 | |
---|
797 | this.nullList.add(null); |
---|
798 | |
---|
799 | - assertNull(this.nullList.findOne(null).getValue()); |
---|
800 | + assertNull(this.nullList.findOne(null)); |
---|
801 | |
---|
802 | this.nullList.add(null); |
---|
803 | |
---|
804 | @@ -344,7 +344,7 @@ |
---|
805 | |
---|
806 | this.mixedList.add(new ListEntry("Sophie", "Ala Bala")); |
---|
807 | |
---|
808 | - assertSame(new ListEntry("Sophie", "Ala Bala").getValue(), this.mixedList.findOne("Sophie").getValue()); |
---|
809 | + assertEquals("Ala Bala", ((ListEntry)this.mixedList.findOne("Sophie")).getValue()); |
---|
810 | |
---|
811 | this.mixedList.add(new ListEntry("Sophie", "Blaaaa")); |
---|
812 | |
---|
813 | @@ -354,8 +354,6 @@ |
---|
814 | } catch (RuntimeException e) { |
---|
815 | // OK. |
---|
816 | } |
---|
817 | - |
---|
818 | - |
---|
819 | } |
---|
820 | |
---|
821 | //removes all elements from all lists. |
---|
822 | @@ -368,4 +366,5 @@ |
---|
823 | this.objectList.clear(); |
---|
824 | } |
---|
825 | |
---|
826 | + // TODO add AutoProperty tests to test tracking |
---|
827 | } |
---|
828 | Index: modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/list/ListEntry.java |
---|
829 | =================================================================== |
---|
830 | --- modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/list/ListEntry.java (revision 2425) |
---|
831 | +++ modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/list/ListEntry.java (working copy) |
---|
832 | @@ -1,19 +1,16 @@ |
---|
833 | package org.sophie2.core.prolib.list; |
---|
834 | |
---|
835 | +import org.sophie2.core.prolib.annot.Immutable; |
---|
836 | + |
---|
837 | /** |
---|
838 | - * A class representing an Entry for lists. |
---|
839 | + * An Immutable class representing an Entry for lists. |
---|
840 | * |
---|
841 | - * @author peko, nenko |
---|
842 | - * |
---|
843 | + * @author peko, nenko, gogov |
---|
844 | */ |
---|
845 | -/** |
---|
846 | - * @author peko |
---|
847 | - * |
---|
848 | - */ |
---|
849 | +@Immutable |
---|
850 | public class ListEntry { |
---|
851 | - |
---|
852 | private final Object key; |
---|
853 | - private Object value; |
---|
854 | + private final Object value; |
---|
855 | |
---|
856 | /** |
---|
857 | * Constructor. |
---|
858 | @@ -35,13 +32,6 @@ |
---|
859 | } |
---|
860 | |
---|
861 | /** |
---|
862 | - * @param value the new value for the entry. |
---|
863 | - */ |
---|
864 | - public void setValue(Object value) { |
---|
865 | - this.value = value; |
---|
866 | - } |
---|
867 | - |
---|
868 | - /** |
---|
869 | * @return the key for this entry. |
---|
870 | */ |
---|
871 | public Object getKey() { |
---|
872 | @@ -50,7 +40,6 @@ |
---|
873 | |
---|
874 | @Override |
---|
875 | public String toString() { |
---|
876 | - return "Key: "+getKey()+" Value: "+getValue(); |
---|
877 | + return "ListEntry: Key=" + getKey() + ", Value=" + getValue(); |
---|
878 | } |
---|
879 | - |
---|
880 | } |
---|
881 | Index: modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/list/BaseProList.java |
---|
882 | =================================================================== |
---|
883 | --- modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/list/BaseProList.java (revision 2425) |
---|
884 | +++ modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/list/BaseProList.java (working copy) |
---|
885 | @@ -10,6 +10,7 @@ |
---|
886 | import java.util.ListIterator; |
---|
887 | import java.util.Map; |
---|
888 | |
---|
889 | +import org.sophie2.core.prolib.Aspect; |
---|
890 | import org.sophie2.core.prolib.BasePro; |
---|
891 | import org.sophie2.core.prolib.ProStatus; |
---|
892 | import org.sophie2.core.prolib.interfaces.Pro; |
---|
893 | @@ -31,13 +32,15 @@ |
---|
894 | * |
---|
895 | * @param <E> |
---|
896 | */ |
---|
897 | + |
---|
898 | public abstract class BaseProList<E> extends BasePro implements ProList<E> { |
---|
899 | |
---|
900 | private final Class<E> elementClass; |
---|
901 | private final CacheList cache; |
---|
902 | private final List<E> roCache; // a read only cache, for public access |
---|
903 | private Pro owner = null; |
---|
904 | - private Map<Object, List<ListEntry>> keyIndex = null; |
---|
905 | + |
---|
906 | + private final ElementsMultimap multimap; |
---|
907 | |
---|
908 | /** |
---|
909 | * Constructor. Tries to auto-detect the element class. |
---|
910 | @@ -93,12 +96,13 @@ |
---|
911 | |
---|
912 | this.cache = new CacheList(mode); |
---|
913 | this.roCache = Collections.unmodifiableList(this.cache); |
---|
914 | - this.keyIndex = new HashMap<Object, List<ListEntry>>(); |
---|
915 | + this.multimap = new ElementsMultimap(); |
---|
916 | + |
---|
917 | resetData(initData); |
---|
918 | } |
---|
919 | |
---|
920 | /** |
---|
921 | - * Immutable mode for a pro list behaviour. |
---|
922 | + * Immutable mode for a ProList behavior. |
---|
923 | * |
---|
924 | * NOTE: this currently supports only "unique" mode, which is basically a |
---|
925 | * hack. |
---|
926 | @@ -107,16 +111,16 @@ |
---|
927 | * |
---|
928 | */ |
---|
929 | public static class ProListMode { |
---|
930 | - private final boolean uniquie; |
---|
931 | + private final boolean unique; |
---|
932 | |
---|
933 | /** |
---|
934 | * Constructor |
---|
935 | * |
---|
936 | * @param unique |
---|
937 | - * whether the elements hould be unique. |
---|
938 | + * whether the elements should be unique. |
---|
939 | */ |
---|
940 | protected ProListMode(boolean unique) { |
---|
941 | - this.uniquie = unique; |
---|
942 | + this.unique = unique; |
---|
943 | } |
---|
944 | |
---|
945 | /** |
---|
946 | @@ -125,7 +129,7 @@ |
---|
947 | * @return the unique mode |
---|
948 | */ |
---|
949 | public boolean isUnique() { |
---|
950 | - return this.uniquie; |
---|
951 | + return this.unique; |
---|
952 | } |
---|
953 | |
---|
954 | /** |
---|
955 | @@ -150,7 +154,8 @@ |
---|
956 | |
---|
957 | private final List<E> inner; |
---|
958 | private final ProListMode mode; |
---|
959 | - |
---|
960 | + private final Aspect sizeAspect; |
---|
961 | + |
---|
962 | /** |
---|
963 | * The internal cache list (with RW access). |
---|
964 | * |
---|
965 | @@ -161,6 +166,7 @@ |
---|
966 | // we can not used checked array, because it does not allow nulls :/ |
---|
967 | this.inner = new ArrayList<E>(); |
---|
968 | this.mode = mode; |
---|
969 | + this.sizeAspect = new Aspect(BaseProList.this); |
---|
970 | } |
---|
971 | |
---|
972 | /** |
---|
973 | @@ -192,6 +198,8 @@ |
---|
974 | @Override |
---|
975 | public int size() { |
---|
976 | registerRead(); |
---|
977 | +// FIXME: size aspect should work |
---|
978 | +// getSizeAspect().registerRead(); |
---|
979 | return getInner().size(); |
---|
980 | } |
---|
981 | |
---|
982 | @@ -208,8 +216,11 @@ |
---|
983 | } |
---|
984 | |
---|
985 | E oldValue = getInner().get(index); |
---|
986 | + |
---|
987 | + // register change to the list |
---|
988 | BaseProListChange change = createSet(index, oldValue, element); |
---|
989 | change.register(); |
---|
990 | + |
---|
991 | registerRead(); |
---|
992 | return oldValue; |
---|
993 | } |
---|
994 | @@ -220,6 +231,7 @@ |
---|
995 | if (getMode().isUnique() && getInner().contains(element)) { |
---|
996 | return; |
---|
997 | } |
---|
998 | + // register change to the list |
---|
999 | BaseProListChange change = createAdd(index, element); |
---|
1000 | change.register(); |
---|
1001 | } |
---|
1002 | @@ -228,8 +240,11 @@ |
---|
1003 | @Override |
---|
1004 | public E remove(int index) { |
---|
1005 | E oldValue = getInner().get(index); |
---|
1006 | + |
---|
1007 | + // register change to the list |
---|
1008 | BaseProListChange change = createRemove(index, oldValue); |
---|
1009 | change.register(); |
---|
1010 | + |
---|
1011 | registerRead(); |
---|
1012 | return oldValue; |
---|
1013 | } |
---|
1014 | @@ -238,11 +253,120 @@ |
---|
1015 | public String toString() { |
---|
1016 | // guess... this does not cause reads :) |
---|
1017 | // why? because every this and that calls it |
---|
1018 | + // (: |
---|
1019 | return this.inner.toString(); |
---|
1020 | } |
---|
1021 | + |
---|
1022 | + /** |
---|
1023 | + * Gets the size {@link Aspect} responsible for this BaseProList. |
---|
1024 | + * @return The size {@link Aspect} responsible for this BaseProList. |
---|
1025 | + */ |
---|
1026 | + public Aspect getSizeAspect() { |
---|
1027 | + return this.sizeAspect; |
---|
1028 | + } |
---|
1029 | } |
---|
1030 | |
---|
1031 | /** |
---|
1032 | + * Helper class for implementing the inner elements multimap of {@link BaseProList}. |
---|
1033 | + * |
---|
1034 | + * @author gogov |
---|
1035 | + */ |
---|
1036 | + protected class ElementsMultimap { |
---|
1037 | + private final Map<Object, List<E>> innerMap; |
---|
1038 | + private final Map<Object, Aspect> aspectsMap; |
---|
1039 | + |
---|
1040 | + /** |
---|
1041 | + * Constructor. |
---|
1042 | + */ |
---|
1043 | + public ElementsMultimap() { |
---|
1044 | + this.innerMap = new HashMap<Object, List<E>>(); |
---|
1045 | + this.aspectsMap = new HashMap<Object, Aspect>(); |
---|
1046 | + } |
---|
1047 | + |
---|
1048 | + /** |
---|
1049 | + * Add an element to the multimap. |
---|
1050 | + * @param element |
---|
1051 | + * The element to be added. |
---|
1052 | + */ |
---|
1053 | + public void add(E element) { |
---|
1054 | + Object key = ProUtil.getKey(element); |
---|
1055 | + if(!this.innerMap.containsKey(key)) { |
---|
1056 | + this.innerMap.put(key, new ArrayList<E>()); |
---|
1057 | + this.aspectsMap.put(key, new Aspect(BaseProList.this)); |
---|
1058 | + } |
---|
1059 | + this.innerMap.get(key).add(element); |
---|
1060 | + } |
---|
1061 | + |
---|
1062 | + /** |
---|
1063 | + * Remove an element from the multimap. |
---|
1064 | + * @param element |
---|
1065 | + * The element to be removed. |
---|
1066 | + */ |
---|
1067 | + public void remove(E element) { |
---|
1068 | + Object key = ProUtil.getKey(element); |
---|
1069 | + if(this.innerMap.containsKey(key)) { |
---|
1070 | + this.innerMap.get(key).remove(element); |
---|
1071 | + } |
---|
1072 | + } |
---|
1073 | + |
---|
1074 | + /** |
---|
1075 | + * Updates a value in the multimap. |
---|
1076 | + * @param oldElement |
---|
1077 | + * The old value which is to be erased. |
---|
1078 | + * @param newElement |
---|
1079 | + * The new value which is to be assigned. |
---|
1080 | + */ |
---|
1081 | + public void set(E oldElement, E newElement) { |
---|
1082 | + remove(oldElement); |
---|
1083 | + add(newElement); |
---|
1084 | + } |
---|
1085 | + |
---|
1086 | + /** |
---|
1087 | + * Returns an unmodifiable {@link List} of all the values |
---|
1088 | + * mapped to a given key. |
---|
1089 | + * @param key |
---|
1090 | + * The key to search values for. |
---|
1091 | + * @return |
---|
1092 | + * An unmodifiable {@link List} of all the values |
---|
1093 | + * mapped to a given key. |
---|
1094 | + */ |
---|
1095 | + public List<E> get(Object key) { |
---|
1096 | + // this is needed |
---|
1097 | + if(!this.innerMap.containsKey(key)) { |
---|
1098 | + this.innerMap.put(key, new ArrayList<E>()); |
---|
1099 | + this.aspectsMap.put(key, new Aspect(BaseProList.this)); |
---|
1100 | + } |
---|
1101 | + |
---|
1102 | + List<E> list = this.innerMap.get(key); |
---|
1103 | + this.aspectsMap.get(key).registerRead(); |
---|
1104 | + |
---|
1105 | + return Collections.unmodifiableList(list); |
---|
1106 | + } |
---|
1107 | + |
---|
1108 | + /** |
---|
1109 | + * Returns the {@link Aspect} responsible for a given key. |
---|
1110 | + * @param key |
---|
1111 | + * The key to find an {@link Aspect} for. |
---|
1112 | + * @return |
---|
1113 | + * The {@link Aspect} responsible for a given key. |
---|
1114 | + */ |
---|
1115 | + public Aspect getAspect(Object key) { |
---|
1116 | + return this.aspectsMap.get(key); |
---|
1117 | + } |
---|
1118 | + |
---|
1119 | + /** |
---|
1120 | + * Gets an unmodifiable view of the inner aspects {@link Map} |
---|
1121 | + * of the {@link BaseProList}; |
---|
1122 | + * @return |
---|
1123 | + * An unmodifiable view of the inner aspects {@link Map} |
---|
1124 | + * of the {@link BaseProList}; |
---|
1125 | + */ |
---|
1126 | + public Map<Object, Aspect> getAspectsMap() { |
---|
1127 | + return Collections.unmodifiableMap(this.aspectsMap); |
---|
1128 | + } |
---|
1129 | + } |
---|
1130 | + |
---|
1131 | + /** |
---|
1132 | * A change on the undеrlying list... |
---|
1133 | * |
---|
1134 | * @author milo |
---|
1135 | @@ -259,12 +383,11 @@ |
---|
1136 | * @param index |
---|
1137 | * the index where it happened |
---|
1138 | * @param oldValue |
---|
1139 | - * the old value (or null if not aplicable) |
---|
1140 | + * the old value (or null if not applicable) |
---|
1141 | * @param newValue |
---|
1142 | - * the new value (or null if not aplicable) |
---|
1143 | + * the new value (or null if not applicable) |
---|
1144 | */ |
---|
1145 | public BaseProListChange(Kind kind, int index, E oldValue, E newValue) { |
---|
1146 | - |
---|
1147 | this.kind = kind; |
---|
1148 | this.index = index; |
---|
1149 | this.oldValue = oldValue; |
---|
1150 | @@ -287,18 +410,19 @@ |
---|
1151 | this.index = index; |
---|
1152 | this.newValue = newValue; |
---|
1153 | switch (kind) { |
---|
1154 | - case ADD: |
---|
1155 | - this.oldValue = null; |
---|
1156 | - break; |
---|
1157 | - case REMOVE: |
---|
1158 | - assert this.newValue == null; |
---|
1159 | - this.oldValue = getSource().cache.inner.get(this.index); |
---|
1160 | - break; |
---|
1161 | - case SET: |
---|
1162 | - this.oldValue = getSource().cache.inner.get(this.index); |
---|
1163 | - break; |
---|
1164 | - default: |
---|
1165 | - assert false; |
---|
1166 | + case ADD: |
---|
1167 | + this.oldValue = null; |
---|
1168 | + break; |
---|
1169 | + case REMOVE: |
---|
1170 | + assert this.newValue == null; |
---|
1171 | + this.oldValue = getSource().getRwCache().inner.get(this.index); |
---|
1172 | + break; |
---|
1173 | + case SET: |
---|
1174 | + this.oldValue = getSource().getRwCache().inner.get(this.index); |
---|
1175 | + break; |
---|
1176 | + default: |
---|
1177 | + assert false; |
---|
1178 | + break; |
---|
1179 | } |
---|
1180 | } |
---|
1181 | |
---|
1182 | @@ -341,7 +465,6 @@ |
---|
1183 | public BaseProList<E> getSource() { |
---|
1184 | return (BaseProList<E>) super.getSource(); |
---|
1185 | } |
---|
1186 | - |
---|
1187 | /** |
---|
1188 | * Modifies a list property to reflect the change. Useful for |
---|
1189 | * synchronizations. |
---|
1190 | @@ -354,7 +477,6 @@ |
---|
1191 | case ADD: |
---|
1192 | target.add(getIndex(), getNewValue()); |
---|
1193 | break; |
---|
1194 | - |
---|
1195 | case REMOVE: |
---|
1196 | target.remove(getIndex()); |
---|
1197 | break; |
---|
1198 | @@ -365,7 +487,6 @@ |
---|
1199 | assert false; |
---|
1200 | break; |
---|
1201 | } |
---|
1202 | - |
---|
1203 | } |
---|
1204 | |
---|
1205 | @Override |
---|
1206 | @@ -397,19 +518,24 @@ |
---|
1207 | public void perform() { |
---|
1208 | logger().trace("performing change: " + this + " on " + fullId()); |
---|
1209 | checkElement(this.newValue); |
---|
1210 | + |
---|
1211 | switch (getKind()) { |
---|
1212 | case ADD: |
---|
1213 | - getSource().cache.inner.add(getIndex(), getNewValue()); |
---|
1214 | - addToKeyIndex(getNewValue()); |
---|
1215 | + getSource().getRwCache().inner.add(getIndex(), getNewValue()); |
---|
1216 | + getSource().getMultimap().add(getNewValue()); |
---|
1217 | break; |
---|
1218 | case REMOVE: |
---|
1219 | - E element = getSource().cache.inner.remove(getIndex()); |
---|
1220 | - removeFromKeyIndex(element); |
---|
1221 | + E element = getSource().getRwCache().inner.remove(getIndex()); |
---|
1222 | + getSource().getMultimap().remove(element); |
---|
1223 | break; |
---|
1224 | case SET: |
---|
1225 | - getSource().cache.inner.set(getIndex(), getNewValue()); |
---|
1226 | - setForKeyInKeyIndex(getSource().cache.inner.get(getIndex()), getNewValue()); |
---|
1227 | + getSource().getRwCache().inner.set(getIndex(), getNewValue()); |
---|
1228 | +// getSource().getMultimap().set(getSource().getRwCache().inner.get(getIndex()), getNewValue()); |
---|
1229 | + getSource().getMultimap().set(getOldValue(), getNewValue()); |
---|
1230 | break; |
---|
1231 | + default: |
---|
1232 | + assert false; |
---|
1233 | + break; |
---|
1234 | } |
---|
1235 | } |
---|
1236 | |
---|
1237 | @@ -424,104 +550,37 @@ |
---|
1238 | return this.reverse; |
---|
1239 | } |
---|
1240 | |
---|
1241 | - @SuppressWarnings("synthetic-access") |
---|
1242 | - private void addToKeyIndex(E element){ |
---|
1243 | - |
---|
1244 | - ListEntry newEntry = null; |
---|
1245 | - if(element == null){ |
---|
1246 | - newEntry = new ListEntry(null, null); |
---|
1247 | - }else if(ListEntry.class.isInstance(element)){ |
---|
1248 | - newEntry = (ListEntry)element; |
---|
1249 | - } |
---|
1250 | - else if(ProUtil.isImmutable(element.getClass())){ |
---|
1251 | - newEntry = new ListEntry(element, element); |
---|
1252 | - } |
---|
1253 | - else{ |
---|
1254 | - newEntry = new ListEntry(null, element); |
---|
1255 | - } |
---|
1256 | - |
---|
1257 | - List<ListEntry> entries = getEntries(newEntry.getKey()); |
---|
1258 | - |
---|
1259 | - entries.add(newEntry); |
---|
1260 | - |
---|
1261 | - } |
---|
1262 | - |
---|
1263 | - @SuppressWarnings("synthetic-access") |
---|
1264 | - private void removeFromKeyIndex(E element){ |
---|
1265 | - |
---|
1266 | - Object keyToRemove = null; |
---|
1267 | - Object valueToRemove = null; |
---|
1268 | - |
---|
1269 | - if(element==null){ |
---|
1270 | - //nothing. both ketToRemove and valueToRemove are null. |
---|
1271 | - }else if(ListEntry.class.isInstance(element)){ |
---|
1272 | - ListEntry entry = (ListEntry)element; |
---|
1273 | - valueToRemove = entry.getValue(); |
---|
1274 | - keyToRemove = entry.getKey(); |
---|
1275 | - |
---|
1276 | - } |
---|
1277 | - else if(ProUtil.isImmutable(element.getClass())){ |
---|
1278 | - keyToRemove = element; |
---|
1279 | - valueToRemove = element; |
---|
1280 | - |
---|
1281 | - } |
---|
1282 | - else{ |
---|
1283 | - valueToRemove = element; |
---|
1284 | - } |
---|
1285 | - |
---|
1286 | - List<ListEntry> entries = getEntries(keyToRemove); |
---|
1287 | - |
---|
1288 | - |
---|
1289 | - ListEntry toRemove = null; |
---|
1290 | - for (ListEntry e : entries) { |
---|
1291 | - if (e.getValue() == valueToRemove |
---|
1292 | - || (e.getValue() != null && e.getValue().equals( |
---|
1293 | - valueToRemove))) { |
---|
1294 | - toRemove = e; |
---|
1295 | - break; |
---|
1296 | - } |
---|
1297 | - } |
---|
1298 | - entries.remove(toRemove); |
---|
1299 | - |
---|
1300 | - |
---|
1301 | - } |
---|
1302 | - |
---|
1303 | - @SuppressWarnings("synthetic-access") |
---|
1304 | - private void setForKeyInKeyIndex(E oldElement, E newElement){ |
---|
1305 | + @Override |
---|
1306 | + protected void fire() { |
---|
1307 | + // fire from the BaseProList |
---|
1308 | + super.fire(); |
---|
1309 | |
---|
1310 | - Object keyToSet = null; |
---|
1311 | - Object oldV = null; |
---|
1312 | - Object newV = null; |
---|
1313 | + // fire from affected Aspects |
---|
1314 | + switch (getKind()) { |
---|
1315 | + case ADD: |
---|
1316 | + getMultimap().getAspect(ProUtil.getKey(getNewValue())).fireChanged(this); |
---|
1317 | +// FIXME: size aspect should work |
---|
1318 | +// getRwCache().getSizeAspect().fireChanged(this); |
---|
1319 | + break; |
---|
1320 | + case REMOVE: |
---|
1321 | + getMultimap().getAspect(ProUtil.getKey(getOldValue())).fireChanged(this); |
---|
1322 | +// FIXME: size aspect should work |
---|
1323 | +// getRwCache().getSizeAspect().fireChanged(this); |
---|
1324 | + break; |
---|
1325 | + case SET: |
---|
1326 | + getMultimap().getAspect(ProUtil.getKey(getNewValue())).fireChanged(this); |
---|
1327 | |
---|
1328 | - if(oldElement==null){ |
---|
1329 | - //nothing. both ketToRemove and valueToRemove are null. |
---|
1330 | - }else if(ListEntry.class.isInstance(oldElement)){ |
---|
1331 | - ListEntry entry = (ListEntry)oldElement; |
---|
1332 | - keyToSet = entry.getKey(); |
---|
1333 | - oldV = entry.getValue(); |
---|
1334 | - newV = (newElement == null ? null : ((ListEntry)newElement).getValue()); |
---|
1335 | - } |
---|
1336 | - else if(ProUtil.isImmutable(oldElement.getClass())){ |
---|
1337 | - keyToSet = oldElement; |
---|
1338 | - oldV = oldElement; |
---|
1339 | - newV = (newElement == null ? null : newElement); |
---|
1340 | - } |
---|
1341 | - else{ |
---|
1342 | - oldV = oldElement; |
---|
1343 | - newV = (newElement == null ? null : newElement); |
---|
1344 | - } |
---|
1345 | - |
---|
1346 | - List<ListEntry> entries = getEntries(keyToSet); |
---|
1347 | - |
---|
1348 | - for(ListEntry e : entries){ |
---|
1349 | - if(e.getValue()==oldV || e.getValue().equals(oldV)){ |
---|
1350 | - e.setValue(newV); |
---|
1351 | - break; |
---|
1352 | + if(!ProUtil.equal(getNewValue(), getOldValue())) { |
---|
1353 | + getMultimap().getAspect(ProUtil.getKey(getOldValue())).fireChanged(this); |
---|
1354 | } |
---|
1355 | + break; |
---|
1356 | + default: |
---|
1357 | + assert false; |
---|
1358 | + break; |
---|
1359 | } |
---|
1360 | } |
---|
1361 | } |
---|
1362 | - |
---|
1363 | + |
---|
1364 | /** |
---|
1365 | * Creates an add change. |
---|
1366 | * |
---|
1367 | @@ -567,7 +626,7 @@ |
---|
1368 | |
---|
1369 | /** |
---|
1370 | * Gets the read-write cache. It is intended to be used by implementors |
---|
1371 | - * only. Changes to it fire apropriate events. |
---|
1372 | + * only. Changes to it fire appropriate events. |
---|
1373 | * |
---|
1374 | * @return the read-write cache. |
---|
1375 | */ |
---|
1376 | @@ -587,6 +646,15 @@ |
---|
1377 | } |
---|
1378 | |
---|
1379 | /** |
---|
1380 | + * Returns the inner {@link ElementsMultimap} for this {@link BaseProList}. |
---|
1381 | + * @return |
---|
1382 | + * The inner {@link ElementsMultimap} for this {@link BaseProList}. |
---|
1383 | + */ |
---|
1384 | + protected ElementsMultimap getMultimap() { |
---|
1385 | + return this.multimap; |
---|
1386 | + } |
---|
1387 | + |
---|
1388 | + /** |
---|
1389 | * Resets the whole data to a new one. Fires appropriate changes. |
---|
1390 | * |
---|
1391 | * @param data |
---|
1392 | @@ -594,15 +662,15 @@ |
---|
1393 | */ |
---|
1394 | @SuppressWarnings("synthetic-access") |
---|
1395 | protected void resetData(List<? extends E> data) { |
---|
1396 | -// This is the old version. Use in case of bugs in the optimized version. |
---|
1397 | +//// This is the old version. Use in case of bugs in the optimized version. |
---|
1398 | // // clear ... fire lots of remove changes |
---|
1399 | // // use internal thing, not to cause self reads... |
---|
1400 | // for (int i = getRwCache().inner.size() - 1; i >= 0; --i) { |
---|
1401 | -// createRemove(i, getRwCache().inner.get(i)).redo(); |
---|
1402 | +// createRemove(i, getRwCache().inner.get(i)).register(); |
---|
1403 | // } |
---|
1404 | // // add new ... |
---|
1405 | // for (int i = 0; i < data.size(); ++i) { |
---|
1406 | -// createAdd(i, data.get(i)).redo(); |
---|
1407 | +// createAdd(i, data.get(i)).register(); |
---|
1408 | // } |
---|
1409 | |
---|
1410 | |
---|
1411 | @@ -792,7 +860,7 @@ |
---|
1412 | return getPublicCache().equals(obj); |
---|
1413 | } |
---|
1414 | |
---|
1415 | - // end of List delagating methods |
---|
1416 | + // end of List delegating methods |
---|
1417 | |
---|
1418 | @SuppressWarnings("synthetic-access") |
---|
1419 | @Override |
---|
1420 | @@ -802,7 +870,7 @@ |
---|
1421 | // There were nasty bugs because of this! |
---|
1422 | try { |
---|
1423 | Registry.get().beginReadTrack(this); |
---|
1424 | - return fullId() + this.cache.inner.toString(); |
---|
1425 | + return fullId() + this.getRwCache().inner.toString(); |
---|
1426 | } finally { |
---|
1427 | Registry.get().endReadTrack(this); |
---|
1428 | |
---|
1429 | @@ -827,7 +895,7 @@ |
---|
1430 | this.owner = owner; |
---|
1431 | |
---|
1432 | } |
---|
1433 | - |
---|
1434 | + |
---|
1435 | /** |
---|
1436 | * Gets the owner's full id (or null if no owner). |
---|
1437 | * |
---|
1438 | @@ -838,31 +906,20 @@ |
---|
1439 | } |
---|
1440 | |
---|
1441 | @Override |
---|
1442 | - public List<ListEntry> findAll(Object key) { |
---|
1443 | - registerRead(); |
---|
1444 | - List<ListEntry> entries = getEntries(key); |
---|
1445 | - return Collections.unmodifiableList(entries); |
---|
1446 | + public List<E> findAll(Object key) { |
---|
1447 | + return getMultimap().get(key); |
---|
1448 | } |
---|
1449 | |
---|
1450 | @Override |
---|
1451 | - public ListEntry findOne(Object key) { |
---|
1452 | - List<ListEntry> entries = findAll(key); |
---|
1453 | - if(entries.size()>1){ |
---|
1454 | - throw new RuntimeException("There are more than one entries with the specified key: "+key+" !!!"); |
---|
1455 | - }else if(entries.size()==0){ |
---|
1456 | - return null; |
---|
1457 | + public E findOne(Object key) { |
---|
1458 | + // XXX |
---|
1459 | + List<E> elements = findAll(key); |
---|
1460 | + if(elements.size() > 1) { |
---|
1461 | + throw new RuntimeException("There are more than one entries with the specified key: " + key + " !!!"); |
---|
1462 | } |
---|
1463 | - return entries.get(0); |
---|
1464 | - } |
---|
1465 | - |
---|
1466 | - private List<ListEntry> getEntries(Object key){ |
---|
1467 | - List<ListEntry> result = this.keyIndex.get(key); |
---|
1468 | - if(result == null){ |
---|
1469 | - result = new ArrayList<ListEntry>(); |
---|
1470 | - this.keyIndex.put(key, result); |
---|
1471 | + else if(elements.size() == 0) { |
---|
1472 | + return null; |
---|
1473 | } |
---|
1474 | - |
---|
1475 | - return result; |
---|
1476 | + return elements.get(0); |
---|
1477 | } |
---|
1478 | - |
---|
1479 | } |
---|
1480 | Index: modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/util/ProUtil.java |
---|
1481 | =================================================================== |
---|
1482 | --- modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/util/ProUtil.java (revision 2425) |
---|
1483 | +++ modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/util/ProUtil.java (working copy) |
---|
1484 | @@ -24,6 +24,7 @@ |
---|
1485 | import org.sophie2.core.prolib.impl.ValueProperty; |
---|
1486 | import org.sophie2.core.prolib.interfaces.ListProp; |
---|
1487 | import org.sophie2.core.prolib.interfaces.ProObject; |
---|
1488 | +import org.sophie2.core.prolib.list.ListEntry; |
---|
1489 | |
---|
1490 | /** |
---|
1491 | * Utils related to pro-objects and properties. |
---|
1492 | @@ -300,4 +301,25 @@ |
---|
1493 | } |
---|
1494 | return a == b; |
---|
1495 | } |
---|
1496 | + |
---|
1497 | + /** |
---|
1498 | + * Helper method for guessing the key of a given ProList item. |
---|
1499 | + * @param listItem |
---|
1500 | + * The given ProList item. |
---|
1501 | + * @return the key of the item. |
---|
1502 | + */ |
---|
1503 | + public static Object getKey(Object listItem) { |
---|
1504 | + if(listItem == null) { |
---|
1505 | + return null; |
---|
1506 | + } |
---|
1507 | + else if(listItem instanceof ListEntry) { |
---|
1508 | + return ((ListEntry)listItem).getKey(); |
---|
1509 | + } |
---|
1510 | + else if(isImmutable(listItem.getClass())) { |
---|
1511 | + return listItem; |
---|
1512 | + } |
---|
1513 | + else { |
---|
1514 | + return null; |
---|
1515 | + } |
---|
1516 | + } |
---|
1517 | } |
---|
1518 | Index: modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/util/Registry.java |
---|
1519 | =================================================================== |
---|
1520 | --- modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/util/Registry.java (revision 2425) |
---|
1521 | +++ modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/util/Registry.java (working copy) |
---|
1522 | @@ -6,6 +6,8 @@ |
---|
1523 | import java.util.IdentityHashMap; |
---|
1524 | import java.util.List; |
---|
1525 | |
---|
1526 | +import org.sophie2.core.prolib.Aspect; |
---|
1527 | +import org.sophie2.core.prolib.impl.AutoTracker; |
---|
1528 | import org.sophie2.core.prolib.interfaces.Pro; |
---|
1529 | |
---|
1530 | |
---|
1531 | @@ -88,9 +90,13 @@ |
---|
1532 | } |
---|
1533 | |
---|
1534 | /** |
---|
1535 | - * Ends a read tracking. |
---|
1536 | + * Ends a read tracking. |
---|
1537 | * @param key The key of this tracking. |
---|
1538 | - * @return All properties which was read. |
---|
1539 | + * @return The list of {@link Pro}s which were read. |
---|
1540 | + * In case both reads of an {@link Aspect}s and its owner are registered, |
---|
1541 | + * the {@link Aspect} is filtered and only the owner is left. |
---|
1542 | + * Thus any listening entities (like {@link AutoTracker}) would get |
---|
1543 | + * updated only once. |
---|
1544 | */ |
---|
1545 | public List<Pro> endReadTrack(Object key) { |
---|
1546 | assert getReadMap().containsKey(key); |
---|
1547 | @@ -99,16 +105,26 @@ |
---|
1548 | List<Pro> track = getReadMap().remove(key).getReads(); |
---|
1549 | getKeyStack().removeLast(); |
---|
1550 | |
---|
1551 | - |
---|
1552 | + // filter out duplicates |
---|
1553 | IdentityHashMap<Pro, Void> unique = new IdentityHashMap<Pro, Void>(); |
---|
1554 | - |
---|
1555 | - |
---|
1556 | + |
---|
1557 | for(Pro p : track){ |
---|
1558 | if(!unique.containsKey(p)) { |
---|
1559 | unique.put(p, null); |
---|
1560 | } |
---|
1561 | } |
---|
1562 | - return new ArrayList<Pro>(unique.keySet()); |
---|
1563 | + |
---|
1564 | + ArrayList<Pro> res = new ArrayList<Pro>(); |
---|
1565 | + |
---|
1566 | + // filter out {@link Aspect}s whose owner was also read |
---|
1567 | + for (Pro pro : unique.keySet()) { |
---|
1568 | + if(!(pro instanceof Aspect && |
---|
1569 | + unique.containsKey(((Aspect)pro).getOwner()))) { |
---|
1570 | + res.add(pro); |
---|
1571 | + } |
---|
1572 | + } |
---|
1573 | + |
---|
1574 | + return res; |
---|
1575 | } |
---|
1576 | |
---|
1577 | /** |
---|
1578 | @@ -131,7 +147,7 @@ |
---|
1579 | }; |
---|
1580 | |
---|
1581 | /** |
---|
1582 | - * Gets the only (currently) instrance of the property registry. |
---|
1583 | + * Gets the only (currently) instance of the property registry. |
---|
1584 | * @return the only (currently) instance of the property registry. |
---|
1585 | */ |
---|
1586 | public static Registry get() { |
---|