GROUP_PRO_LIB_PERFORMANCE_R0: 2009-05-09-15-29 patch

File 2009-05-09-15-29 patch, 24.0 KB (added by gogov, 16 years ago)

latest patch. size aspect works!

Line 
1### Eclipse Workspace Patch 1.0
2#P sophie2-platform
3Index: modules/org.sophie2.core/src/test/java/org/sophie2/core/prolib/list/BaseProListAspectsTest.java
4===================================================================
5--- modules/org.sophie2.core/src/test/java/org/sophie2/core/prolib/list/BaseProListAspectsTest.java     (revision 2504)
6+++ modules/org.sophie2.core/src/test/java/org/sophie2/core/prolib/list/BaseProListAspectsTest.java     (working copy)
7@@ -15,6 +15,7 @@
8 import org.sophie2.core.prolib.interfaces.Pro;
9 import org.sophie2.core.prolib.interfaces.Prop;
10 import org.sophie2.core.prolib.interfaces.RwListProp;
11+import org.sophie2.core.prolib.list.BaseProList.CacheList;
12 import org.sophie2.core.prolib.util.ChangeManager;
13 import org.sophie2.core.prolib.util.DefaultChangeManager;
14 import org.sophie2.core.prolib.util.Registry;
15@@ -22,31 +23,63 @@
16 
17 /**
18  * El mucho grande test for {@link Aspect}s usage with {@link BaseProList}s.
19- * Test is meant to work with the size {@link Aspect} enabled and it does!
20- * Currently size {@link Aspect} is disabled so this test fails.
21- * Will fix when size {@link Aspect} is fixed. --gogov 2009-05-08 8:56 PM
22  *
23  * @author gogov
24  */
25-@SuppressWarnings("all")
26+
27+@SuppressWarnings("synthetic-access")
28 public class BaseProListAspectsTest extends UnitTestBase {
29+       /**
30+        * A class which extends {@link BaseProList} for testing purposes.
31+        * Exposes access to {@link Aspect}s for assertion purposes.
32+        * @author gogov
33+        *
34+        * @param <T>
35+        *                      The type of the elements this list holds.
36+        */
37        class TestProList<T> extends BaseProList<T> {
38+               /**
39+                * Constructor.
40+                */
41                public TestProList() {
42                        this.new StatusChange(ProStatus.READY).register();
43                }
44               
45+               /**
46+                * Exposes the size {@link Aspect}.
47+                *
48+                * @return The size {@link Aspect} of the {@link TestProList}.
49+                */
50                public Aspect getSizeAspect() {
51                        return getRwCache().getSizeAspect();
52                }
53               
54+               /**
55+                * Exposes the inner {@link Aspect}s {@link Map} of the {@link TestProList}.
56+                *
57+                * @return The inner {@link Aspect}s {@link Map} of the {@link TestProList}.
58+                */
59                public Map<Object, Aspect> getKeyAspectsMap() {
60                        return getMultimap().getAspectsMap();
61                }
62               
63+               /**
64+                * Gets a {@link List} of all key {@link Aspect}s of the {@link TestProList}
65+                *
66+                * @return A {@link List} of all key {@link Aspect}s of the {@link TestProList}
67+                */
68                public List<Aspect> getKeyAspects() {
69                        return new ArrayList<Aspect>(getKeyAspectsMap().values());
70                }
71               
72+               /**
73+                * Gets a {@link List} of all (including size) {@link Aspect}s of
74+                * the {@link TestProList}
75+                *
76+                * @return A {@link List} of all (including size) {@link Aspect}s of
77+                * the {@link TestProList}
78+                */
79+
80                public List<Aspect> getAllAspects() {
81                        List<Aspect> res = getKeyAspects();
82                        res.add(0, getSizeAspect());
83@@ -71,41 +104,45 @@
84               
85        }
86 
87-       abstract class ReadsHelper {
88+       private abstract class ReadsHelper {
89                public List<Pro> run() {
90-                       try  {
91+                       List<Pro> reads = new ArrayList<Pro>();
92+                       try {
93                                Registry.get().beginReadTrack(this);
94                                method();
95                        }
96-                       finally  {
97-                               return Registry.get().endReadTrack(this);
98+                       finally {
99+                               reads = Registry.get().endReadTrack(this);
100                        }
101+                       return reads;
102                }
103 
104                public List<Pro> run(int expectedReadsCount) {
105+                       List<Pro> reads = new ArrayList<Pro>();
106                        try  {
107                                Registry.get().beginReadTrack(this);
108                                method();
109                        }
110                        finally  {
111-                               List<Pro> reads = Registry.get().endReadTrack(this);
112+                               reads = Registry.get().endReadTrack(this);
113                                assertEquals(expectedReadsCount, reads.size());
114-                               
115-                               return reads;
116                        }
117+                       
118+                       return reads;
119                }               
120               
121                public List<Pro> run(List<Pro> expectedReadPros) {
122+                       List<Pro> reads = new ArrayList<Pro>();
123                        try  {
124                                Registry.get().beginReadTrack(this);
125                                method();
126                        }
127                        finally  {
128-                               List<Pro> reads = Registry.get().endReadTrack(this);
129+                               reads = Registry.get().endReadTrack(this);
130                                assertSameContent(expectedReadPros, reads);
131-                               
132-                               return reads;
133                        }
134+                       
135+                       return reads;
136                }               
137 
138                public List<Pro> run(Pro ... expectedReadsPro) {
139@@ -116,91 +153,113 @@
140                abstract protected void method();
141        }
142 
143-       private TestProList beers = new TestProList<String>();
144+       private TestProList<String> beers = new TestProList<String>();
145+
146+       /**
147+        * Gets a sample {@link TestProList}.
148+        * @return A sample {@link TestProList}.
149+        */
150+       public TestProList<String> beers() {
151+               return this.beers;
152+       }
153       
154+       /**
155+        * Tests the reads behavior of the inner {@link CacheList} of the
156+        * {@link TestProList}.
157+        */
158        @Test
159        public void testCacheListAspectsReads() {
160                new ReadsHelper() {
161                        @Override
162                        protected void method() {
163-                               beers.add("zagorka"); // read of size aspect
164-                               beers.add("kamenitza"); // read of size aspect
165-                               beers.add("pirinsko"); // read of size aspect
166-                               beers.add("shumensko"); // read of size aspect
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+               }.run(beers().getSizeAspect());
174 
175                new ReadsHelper() {
176                        @Override
177                        protected void method() {
178-                               beers.set(0, "stolichno"); // read of root aspect                       
179+                               beers().set(0, "stolichno"); // read of root aspect                     
180                        }
181-               }.run((Pro)beers);
182+               }.run((Pro)beers());
183               
184                new ReadsHelper() {
185                        @Override
186                        protected void method() {
187-                               beers.remove(0); // read of root aspect                 
188+                               beers().remove(0); // read of root aspect                       
189                        }
190-               }.run((Pro)beers);
191+               }.run((Pro)beers());
192 
193                new ReadsHelper() {
194                        @Override
195                        protected void method() {
196-                               beers.size(); // read of size aspect                   
197+                               beers().size(); // read of size aspect                 
198                        }
199-               }.run(beers.getSizeAspect());
200+               }.run(beers().getSizeAspect());
201               
202                new ReadsHelper() {
203                        @Override
204                        protected void method() {
205-                               beers.get(0); // read of root aspect
206+                               beers().get(0); // read of root aspect
207                        }
208-               }.run((Pro)beers);
209+               }.run((Pro)beers());
210        }
211-
212+       /**
213+        * Tests the reads behavior of the inner key {@link Aspect}s of the
214+        * {@link TestProList}.
215+        */
216        @Test
217        public void testKeyAspectsReads() {
218-               beers.add("zagorka");
219-               beers.add("zagorka");
220-               beers.add("kamenitza");
221-               beers.add("pirinsko");
222-               beers.add("shumensko");
223-               beers.set(0, "stolichno");
224-               beers.remove(0);
225-               beers.size();
226+               beers().add("zagorka");
227+               beers().add("zagorka");
228+               beers().add("kamenitza");
229+               beers().add("pirinsko");
230+               beers().add("shumensko");
231+               beers().set(0, "stolichno");
232+               beers().remove(0);
233+               beers().size();
234               
235                List<Pro> reads =
236                new ReadsHelper() {
237                        @Override
238                        protected void method() {
239-                               beers.findAll("");
240-                               beers.findAll("zagorka");
241-                               beers.findAll("meddle");
242-                               beers.findOne("meddle");
243-                               beers.findAll("meddle");                       
244-                               beers.findOne("non-existing-key");
245-                               beers.findOne("non-existing-key");
246-                               beers.findOne("non-existing-key");
247+                               beers().findAll("");
248+                               beers().findAll("zagorka");
249+                               beers().findAll("meddle");
250+                               beers().findOne("meddle");
251+                               beers().findAll("meddle");                     
252+                               beers().findOne("non-existing-key");
253+                               beers().findOne("non-existing-key");
254+                               beers().findOne("non-existing-key");
255                        }
256                }.run();
257 
258                assertSameContent(
259-                               new ArrayList(values(beers.getKeyAspectsMap(),
260+                               new ArrayList<Object>(values(beers().getKeyAspectsMap(),
261                                                "", "zagorka", "meddle", "non-existing-key")),
262                                reads);
263        }
264 
265-       class JunkProObject extends BaseProObject {
266+       private class JunkProObject extends BaseProObject {
267                public RwListProp<Object> records() {
268                        return getBean().makeListProp("records", Object.class);
269                }
270        }
271 
272-       final JunkProObject junkie = new JunkProObject();
273+       final private JunkProObject junkie = new JunkProObject();
274       
275+       /**
276+        * Gets a sample {@link JunkProObject}.
277+        * @return A sample {@link JunkProObject}.
278+        */
279+       public JunkProObject junkie() {
280+               return this.junkie;
281+       }       
282 
283-       abstract class AutoHelper extends BaseProObject {
284+       private abstract class AutoHelper extends BaseProObject {
285                private int calls;
286               
287                public AutoHelper() {
288@@ -213,6 +272,7 @@
289               
290                public Prop<Integer> prop() {
291                        class prop extends AutoProperty<Integer> {
292+                               @Override
293                                protected Integer compute() {
294                                        ++AutoHelper.this.calls;
295                                        method();
296@@ -225,6 +285,12 @@
297                abstract protected void method();
298        }
299       
300+       /**
301+        * Test the number of updates certain AutoProperties do given a series of various
302+        * operations on a target {@link BaseProList}.
303+        * This way we ensure they get updated always when they need to get updated, and
304+        * that they don't get updated too many times.
305+        */
306        @Test
307        public void testAutoPropertyCalls() {
308                AutoHelper h0 = new AutoHelper() {
309@@ -237,42 +303,43 @@
310                AutoHelper h1 = new AutoHelper() {
311                        @Override
312                        protected void method() {
313-                               junkie.records().get().size();
314+                               junkie().records().get().size();
315                        }
316                };
317               
318                AutoHelper h2 = new AutoHelper() {
319                        @Override
320                        protected void method() {
321-                               junkie.records().get().findAll("person");
322+                               junkie().records().get().findAll("person");
323                        }
324                };
325 
326                AutoHelper h3 = new AutoHelper() {
327                        @Override
328                        protected void method() {
329-                               junkie.records().get().findAll("beer");
330+                               junkie().records().get().findAll("beer");
331                        }
332                };
333 
334                AutoHelper h4 = new AutoHelper() {
335                        @Override
336                        protected void method() {
337-                               junkie.records().get().findAll(null);
338+                               junkie().records().get().findAll(null);
339                        }
340                };
341 
342                AutoHelper h5 = new AutoHelper() {
343                        @Override
344                        protected void method() {
345-                               junkie.records().get().findAll(new Integer(6));
346+                               junkie().records().get().findAll(new Integer(6));
347                        }
348                };
349 
350+               @SuppressWarnings("unused")
351                AutoHelper h6 = new AutoHelper() {
352                        @Override
353                        protected void method() {
354-                               for(Object obj : junkie.records().get()) {
355+                               for(Object obj : junkie().records().get()) {
356                                        // nothing
357                                }
358                        }
359@@ -281,31 +348,31 @@
360                AutoHelper h7 = new AutoHelper() {
361                        @Override
362                        protected void method() {
363-                               junkie.records().get().size();
364-                               junkie.records().get().findAll("beer");
365+                               junkie().records().get().size();
366+                               junkie().records().get().findAll("beer");
367                        }
368                };
369 
370                AutoHelper h8 = new AutoHelper() {
371                        @Override
372                        protected void method() {
373-                               junkie.records().get().findAll("junk-key1");
374-                               junkie.records().get().findAll("junk-key2");
375-                               junkie.records().get().findAll("junk-key3");
376-                               junkie.records().get().findAll("junk-key4");
377-                               junkie.records().get().findAll("junk-key5");
378-                               junkie.records().get().findAll("junk-key6");                           
379+                               junkie().records().get().findAll("junk-key1");
380+                               junkie().records().get().findAll("junk-key2");
381+                               junkie().records().get().findAll("junk-key3");
382+                               junkie().records().get().findAll("junk-key4");
383+                               junkie().records().get().findAll("junk-key5");
384+                               junkie().records().get().findAll("junk-key6");                         
385                        }
386                };             
387               
388                AutoHelper h9 = new AutoHelper() {
389                        @Override
390                        protected void method() {
391-                               junkie.records().get().size();
392-                               junkie.records().get().findAll("beer");
393-                               junkie.records().get().findOne(new Integer(6));
394-                               junkie.records().get().findAll("never-added");
395-                               junkie.records().get().findAll("meddle");
396+                               junkie().records().get().size();
397+                               junkie().records().get().findAll("beer");
398+                               junkie().records().get().findOne(new Integer(6));
399+                               junkie().records().get().findAll("never-added");
400+                               junkie().records().get().findAll("meddle");
401                        }
402                };
403               
404@@ -320,30 +387,30 @@
405                h8.prop().get();
406                h9.prop().get();
407               
408-               junkie.records().add(new ListEntry("person", "meddle"));
409-               junkie.records().add(new ListEntry("person", "pique"));
410-               junkie.records().add(new ListEntry("person", "drogba"));
411-               junkie.records().add(new ListEntry("person", "gay-referee"));
412-               junkie.records().add(new ListEntry("beer", "zagorka"));
413-               junkie.records().add(Integer.valueOf(6));
414-               junkie.records().add("meddle");
415-               junkie.records().add(null);             
416-               junkie.records().add(new ListEntry("junk-key1", 5));
417-               junkie.records().add(new ListEntry("junk-key2", new Object()));
418-               junkie.records().add(new ListEntry("junk-key3", "jeha"));
419-               junkie.records().add(new ListEntry("junk-key4", Color.black));
420-               junkie.records().add(new ListEntry("junk-key5", 'a'));         
421-               junkie.records().add(new ListEntry("junk-key6", 123.543));
422-               junkie.records().remove(3);             
423-               junkie.records().remove(5);             
424-               junkie.records().remove(1);
425-               junkie.records().remove(0);
426-               junkie.records().remove(0);             
427-               junkie.records().set(0, "meddle");
428-               junkie.records().set(2, new ListEntry("person", "o'sullivan"));         
429-               junkie.records().set(3, "meddle");             
430-               junkie.records().set(2, new ListEntry("beer", "stolichno"));           
431-               junkie.records().set(5, "meddle");             
432+               junkie().records().add(new ListEntry("person", "meddle"));
433+               junkie().records().add(new ListEntry("person", "pique"));
434+               junkie().records().add(new ListEntry("person", "drogba"));
435+               junkie().records().add(new ListEntry("person", "gay-referee"));
436+               junkie().records().add(new ListEntry("beer", "zagorka"));
437+               junkie().records().add(Integer.valueOf(6));
438+               junkie().records().add("meddle");
439+               junkie().records().add(null);           
440+               junkie().records().add(new ListEntry("junk-key1", 5));
441+               junkie().records().add(new ListEntry("junk-key2", new Object()));
442+               junkie().records().add(new ListEntry("junk-key3", "jeha"));
443+               junkie().records().add(new ListEntry("junk-key4", Color.black));
444+               junkie().records().add(new ListEntry("junk-key5", 'a'));               
445+               junkie().records().add(new ListEntry("junk-key6", 123.543));
446+               junkie().records().remove(3);           
447+               junkie().records().remove(5);           
448+               junkie().records().remove(1);
449+               junkie().records().remove(0);
450+               junkie().records().remove(0);           
451+               junkie().records().set(0, "meddle");
452+               junkie().records().set(2, new ListEntry("person", "o'sullivan"));               
453+               junkie().records().set(3, "meddle");           
454+               junkie().records().set(2, new ListEntry("beer", "stolichno"));         
455+               junkie().records().set(5, "meddle");           
456                               
457                h0.prop().get();
458                h1.prop().get();
459@@ -369,6 +436,16 @@
460                assertEquals(29, h9.getCalls());               
461        }
462       
463+       /**
464+        * Helper method which gets a {@link Collection} of all the corresponding values
465+        * of a given {@link Collection} of keys in a given {@link Map}.
466+        * @param <K> The type of the keys.
467+        * @param <V> The type of the values.
468+        * @param map The {@link Map} which holds the mappings.
469+        * @param keys The input {@link Collection} of keys.
470+        * @return A {@link Collection} of all the corresponding values of the given keys in
471+        *                      the given map.
472+        */
473        public static <K, V> Collection<V> values(Map<K, V> map, K ... keys) {
474                Collection<V> c = new ArrayList<V>();
475 
476Index: modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/BasePro.java
477===================================================================
478--- modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/BasePro.java (revision 2500)
479+++ modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/BasePro.java (working copy)
480@@ -49,7 +49,6 @@
481         *
482         */
483        protected void registerRead() {
484-               
485                Registry.get().registerRead(this);
486               
487                if(getLastState() == ProStatus.CREATED) {
488@@ -119,11 +118,11 @@
489         * @param change
490         *            The change to be sent.
491         */
492-       private void fireChanged(ProChange change) {
493+       protected void fireChanged(ProChange change) {
494                assert change.getSource() == this;
495                Object key = new Object();
496                try {
497-                       // ignore all reades caused by changes
498+                       // ignore all reads caused by changes
499                        Registry.get().beginReadTrack(key);
500                        changed(change);
501                        List<ProListener> list = new ArrayList<ProListener>(
502@@ -166,7 +165,7 @@
503                public BaseProChange(BasePro source) {
504                        super(source);
505 
506-                       // and check whether hasEffect is overriden in the bottom level
507+                       // and check whether hasEffect is overridden in the bottom level
508                        // class
509                        try {
510                                getClass().getDeclaredMethod("hasEffect");
511@@ -183,11 +182,9 @@
512                        super(BasePro.this);
513                }
514 
515-               @SuppressWarnings("synthetic-access")
516                @Override
517                protected void fire() {
518                        getSource().fireChanged(this);
519-
520                }
521 
522                @Override
523@@ -220,7 +217,7 @@
524        }
525 
526        /**
527-        * A change of the state of a BasePro. Implementors, dont forget ot override
528+        * A change of the state of a BasePro. Implementors, don't forget to override
529         * {@link #hasEffect()}!
530         *
531         * @author milo
532@@ -244,15 +241,15 @@
533                public StatusChange(BasePro source, ProStatus oldStatus,
534                                ProStatus newStatus) {
535                        super(source);
536-                       assert newStatus.isMetaAvailable(); //can not revert
537-                       //to crreated
538+                       assert newStatus.isMetaAvailable(); // can not revert
539+                       // to created
540                        this.oldStatus = oldStatus;
541                        this.newStatus = newStatus;
542                       
543                }
544 
545                /**
546-                * Simpler constructor. Uses the state of the eclosing class for
547+                * Simpler constructor. Uses the state of the enclosing class for
548                 * oldStatus.
549                 *
550                 * @param newStatus
551Index: modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/util/ReflectionUtil.java
552===================================================================
553--- modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/util/ReflectionUtil.java     (revision 2504)
554+++ modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/util/ReflectionUtil.java     (working copy)
555@@ -55,13 +55,13 @@
556                                + clName.substring(beg + 1);
557        }
558 
559-       private static <T extends Annotation> T findMethodAnnotation(Method m,
560-                       Class<?> c, Class<T> annotationClass) {
561-               if (c.equals(Object.class)) {
562+       private static <T extends Annotation> T findMethodAnnotation(Method method,
563+                       Class<?> cl, Class<T> annotationClass) {
564+               if (cl.equals(Object.class)) {
565                        return null;
566                }
567                try {
568-                       T res = c.getDeclaredMethod(m.getName(), m.getParameterTypes())
569+                       T res = cl.getDeclaredMethod(method.getName(), method.getParameterTypes())
570                                        .getAnnotation(annotationClass);
571                        if (res != null) {
572                                return res;
573@@ -73,10 +73,15 @@
574                        // logger().warn("",e);
575                        // ignore
576                }
577-               return findMethodAnnotation(m, c.getSuperclass(), annotationClass);
578+               return findMethodAnnotation(method, cl.getSuperclass(), annotationClass);
579        }
580       
581-       
582+       /**
583+        * Represents a query for findMethodAnnotation().
584+        * Used for memoization of the results and optimized performance.
585+        *
586+        * @author milo
587+        */
588        private static class AnnoQuery {
589                private final Method method;
590                private final Class<?> annoClass;
591@@ -124,6 +129,10 @@
592                }
593               
594        }
595+       
596+       /**
597+        * A map used for memoizing query results of findMethodAnnotation()
598+        */
599        private static Map<AnnoQuery, Object> annoCache = new HashMap<AnnoQuery, Object>();
600 
601        /**
602@@ -131,19 +140,20 @@
603         * is not present, returns null.
604         *
605         * @param <T> The annotation type.
606-        * @param m The method to search.
607+        * @param method The method to search.
608         * @param annotationClass the annotation class.
609         * @return the found annotation, or null.
610         */
611+
612        @SuppressWarnings("unchecked")
613-       public static <T extends Annotation> T findMethodAnnotation(Method m,
614+       public static <T extends Annotation> T findMethodAnnotation(Method method,
615                        Class<T> annotationClass) {
616-               AnnoQuery q = new AnnoQuery(m, annotationClass);
617+               AnnoQuery q = new AnnoQuery(method, annotationClass);
618                if(!annoCache.containsKey(q)) {
619-                       T res = findMethodAnnotation(m, m.getDeclaringClass(), annotationClass);
620+                       T res = findMethodAnnotation(method, method.getDeclaringClass(), annotationClass);
621                        annoCache.put(q, res);
622                }
623-               return (T) annoCache.get(q);
624+               return (T)annoCache.get(q);
625        }
626 
627        /**
628@@ -151,14 +161,14 @@
629         * return type is a property, and it does not have a {@link NoProp}
630         * annotation in its hierarchy.
631         *
632-        * @param m
633+        * @param method
634         *            The method.
635         * @return Whether it is property.
636         */
637-       public static boolean isPropMethod(Method m) {
638-               return m.getParameterTypes().length == 0
639-                               && Prop.class.isAssignableFrom(m.getReturnType())
640-                               && !(findMethodAnnotation(m, NoProp.class) != null);
641+       public static boolean isPropMethod(Method method) {
642+               return method.getParameterTypes().length == 0
643+                               && Prop.class.isAssignableFrom(method.getReturnType())
644+                               && !(findMethodAnnotation(method, NoProp.class) != null);
645        }
646 
647        /**
648Index: modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/list/ListEntry.java
649===================================================================
650--- modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/list/ListEntry.java  (revision 2504)
651+++ modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/list/ListEntry.java  (working copy)
652@@ -36,7 +36,6 @@
653 
654        /**
655         * Getter of the key.
656-        *
657         * @return The key for this entry.
658         */
659        public Object getKey() {
660Index: modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/list/BaseProList.java
661===================================================================
662--- modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/list/BaseProList.java        (revision 2504)
663+++ modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/list/BaseProList.java        (working copy)
664@@ -194,19 +194,15 @@
665                        return getInner().get(index);
666                }
667 
668-               @SuppressWarnings("synthetic-access")
669                @Override
670                public int size() {
671-                       registerRead();
672-//                     FIXME: size aspect should work
673-//                     getSizeAspect().registerRead();
674+                       getSizeAspect().registerRead();
675                        return getInner().size();
676                }
677 
678                @SuppressWarnings("synthetic-access")
679                @Override
680                public E set(int index, E element) {
681-
682                        // TODO this unique thing is a hack
683                        if (getMode().isUnique()) {
684                                E oldValue = remove(index);
685@@ -559,13 +555,11 @@
686                        switch (getKind()) {
687                        case ADD:
688                                getMultimap().getAspect(ProUtil.getKey(getNewValue())).fireChanged(this);
689-//                             FIXME: size aspect should work
690-//                             getRwCache().getSizeAspect().fireChanged(this);
691+                               getRwCache().getSizeAspect().fireChanged(this);
692                                break;
693                        case REMOVE:
694                                getMultimap().getAspect(ProUtil.getKey(getOldValue())).fireChanged(this);                               
695-//                             FIXME: size aspect should work
696-//                             getRwCache().getSizeAspect().fireChanged(this);
697+                               getRwCache().getSizeAspect().fireChanged(this);
698                                break;
699                        case SET:
700                                getMultimap().getAspect(ProUtil.getKey(getNewValue())).fireChanged(this);
701@@ -912,7 +906,6 @@
702 
703        @Override
704        public E findOne(Object key) {
705-               // XXX
706                List<E> elements = findAll(key);
707                if(elements.size() > 1) {
708                        throw new RuntimeException("There are more than one entries with the specified key: " + key + " !!!");
709Index: modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/Aspect.java
710===================================================================
711--- modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/Aspect.java  (revision 2504)
712+++ modules/org.sophie2.core/src/main/java/org/sophie2/core/prolib/Aspect.java  (working copy)
713@@ -3,7 +3,6 @@
714 import java.util.ArrayList;
715 import java.util.List;
716 
717-import org.sophie2.core.prolib.errors.NotAvailableException;
718 import org.sophie2.core.prolib.events.ProChange;
719 import org.sophie2.core.prolib.events.ProListener;
720 import org.sophie2.core.prolib.list.BaseProList;
721@@ -62,15 +61,24 @@
722        public void registerRead() {
723                Registry.get().registerRead(this);
724               
725-               if(getOwner().getLastState() == ProStatus.CREATED) {
726-                       throw new NotAvailableException(fullId() + " attributes not initialized");
727+               // if owner is in state READY, it's ok to attach listeners to the {@link Aspect}
728+               if(getOwner().getLastState().isReady()) {
729+                       Registry.get().registerRead(this);
730                }
731-               else if(getOwner().getLastState() == ProStatus.DESTROYED) {
732-                       throw new NotAvailableException(fullId() + " already destroyed!");
733+               // otherwise though, the {@link Aspect} is not in a usable condition, so the following
734+               // scenario might occur:
735+               // 1) someone accesses the {@link Aspect} while it's not READY
736+               // 2) then its owner becomes READY but doesn't notify the {@link Aspect}
737+               //    that it's READY, so a {@link NotAvailable} exception is thrown
738+               //    when it shouldn't be.
739+               //
740+               // registering a read to the owner fixes things though, because the Aspect attaches
741+               // a listener to the owner and gets updated when the owner's READY.
742+               //
743+               // there were nasty issues because of this --gogov 2009-05-09 14:48
744+               else {
745+                       getOwner().registerRead();
746                }
747-               else if(getOwner().getLastState() == ProStatus.META_INITIALIZED) {
748-                       throw new NotAvailableException(fullId() + " value not initialized!", lastProblem());
749-               }               
750        }
751       
752        @Override
753