plugs can now inherit
authorClaus Gittinger <cg@exept.de>
Tue, 14 Feb 2006 12:05:34 +0100
changeset 2100 17cc7c9aa82d
parent 2099 306e2edb75bf
child 2101 58a78b51f78e
plugs can now inherit
Plug.st
--- a/Plug.st	Mon Feb 13 09:55:37 2006 +0100
+++ b/Plug.st	Tue Feb 14 12:05:34 2006 +0100
@@ -13,7 +13,7 @@
 "{ Package: 'stx:libview2' }"
 
 Model subclass:#Plug
-	instanceVariableNames:'simulatedProtocol'
+	instanceVariableNames:'simulatedProtocol inheritedClasses'
 	classVariableNames:''
 	poolDictionaries:''
 	category:'Kernel-Objects'
@@ -39,7 +39,9 @@
 "
     A Plug is an object which simulates a protocol and evaluates
     a corresponding block when receiving messages.
-    A plugs interface can be changed dynamically.
+    A plug's interface can be changed dynamically.
+    A plug can also be told to simulate inheriting messages from other classes,
+    even multiple inheritance is possible.
 
     Its main use is for the demo doIts, to play the role of a model,
     when no actual modelClass is available for the demonstration.
@@ -60,55 +62,73 @@
 
 examples
 "
+  a simple plug:
                                                                         [exBegin]
-    |plug|
+        |plug|
 
-    plug := Plug new.
-    plug respondTo:#foo  with:[Transcript showCR:'received foo'].
-    plug respondTo:#foo: with:[:arg | Transcript showCR:'received foo: ', arg printString].
+        plug := Plug new.
+        plug respondTo:#foo  with:[Transcript showCR:'Plug received foo'].
+        plug respondTo:#foo: with:[:arg | Transcript showCR:'Plug received foo: ', arg printString].
 
-    plug foo.
-    plug foo:'some argument'
+        plug foo.
+        plug foo:'some argument'
                                                                         [exEnd]
 
   using a plug as a generator (simulates a readStream):
                                                                         [exBegin]
-    |generator num|
+        |generator num|
 
-    num := 0.
-    generator := Plug new.
-    generator respondTo:#next
-                   with:[num := num + 1. num].
-    generator respondTo:#atEnd
-                   with:[false].
+        num := 0.
+        generator := Plug new.
+        generator respondTo:#next   with:[num := num + 1. num].
+        generator respondTo:#atEnd  with:[false].
 
-    10 timesRepeat:[
-        Transcript showCR:(generator next)
-    ]
+        10 timesRepeat:[
+            Transcript showCR:(generator next)
+        ]
                                                                         [exEnd]
 
   simulating ``instance variables'':
   (actually, this is somewhat expensive - the contexts locals are used for them ...)
   be careful with unintended variable sharing (if plugs are created in a loop ..)
                                                                         [exBegin]
-    |plug1 plug2 local1 local2|
+        |plug1 plug2 local1 local2|
+
+        plug1 := Plug new.
+        plug1 respondTo:#get  with:[local1].
+        plug1 respondTo:#set: with:[:arg | local1 := arg].
 
-    plug1 := Plug new.
-    plug1 respondTo:#get  with:[local1].
-    plug1 respondTo:#set: with:[:arg | local1 := arg].
+        plug2 := Plug new.
+        plug2 respondTo:#get  with:[local2].
+        plug2 respondTo:#set: with:[:arg | local2 := arg].
+
+        Transcript show:'plug1''s value: '; showCR:plug1 get.
+        Transcript show:'plug2''s value: '; showCR:plug2 get.
+
+        plug1 set:5.
+        plug2 set:17.
 
-    plug2 := Plug new.
-    plug2 respondTo:#get  with:[local2].
-    plug2 respondTo:#set: with:[:arg | local2 := arg].
+        Transcript show:'plug1''s value: '; showCR:plug1 get.
+        Transcript show:'plug2''s value: '; showCR:plug2 get.
+                                                                        [exEnd]
+
+  simulating a big list in a ListView 
+  (real applications would read the lines from a database or file):
+                                                                        [exBegin]
+        |virtualList top lv|
 
-    Transcript show:'plug1''s value: '; showCR:plug1 get.
-    Transcript show:'plug2''s value: '; showCR:plug2 get.
+        virtualList := Plug new.
+        virtualList inheritFrom:SequenceableCollection.
+        virtualList respondTo:#size with:[ 1000000 ].
+        virtualList respondTo:#at:  with:[:lineNr | 'List line Nr. ' , lineNr printString ].
 
-    plug1 set:5.
-    plug2 set:17.
+        top := StandardSystemView extent:200@200.
 
-    Transcript show:'plug1''s value: '; showCR:plug1 get.
-    Transcript show:'plug2''s value: '; showCR:plug2 get.
+        lv := ScrollableView for:ListView in:top.
+        lv origin:0.0 @ 0.0 corner:1.0 @ 1.0. 
+        lv list:virtualList expandTabs:false scanForNonStrings:false includesNonStrings:false.
+
+        top open.
                                                                         [exEnd]
 "
 ! !
@@ -153,14 +173,30 @@
     |block|
 
     block := simulatedProtocol at:(aMessage selector) ifAbsent:[].
-    block isNil ifTrue:[
-        ^ super doesNotUnderstand:aMessage
+    block notNil ifTrue:[
+        ^ block valueWithArguments:(aMessage arguments)
     ].
-    ^ block valueWithArguments:(aMessage arguments)
+    inheritedClasses notNil ifTrue:[
+        inheritedClasses do:[:eachClass |
+            |method|
+
+            method := eachClass lookupMethodFor:(aMessage selector).
+            method notNil ifTrue:[
+                ^ method valueWithReceiver:self arguments:(aMessage arguments).
+            ].
+        ].
+    ].
+    ^ super doesNotUnderstand:aMessage
 
     "Modified: 27.4.1996 / 16:15:34 / cg"
 !
 
+size
+    "catch this one - its so common"
+
+    ^ self doesNotUnderstand:(Message selector:#size arguments:#())
+!
+
 update:something with:aParameter from:changedObject
     "catch unhandled messages by looking in my simulated protocol
      definition; if there is some block for it, return its value.
@@ -222,6 +258,21 @@
     "Created: 27.4.1996 / 16:19:08 / cg"
 !
 
+inheritFrom:aClass
+    "very tricky - change the inheritance.
+     This cannot be done by changing my class directly, because the instance layout
+     must still be correct for Plugs instance variables.
+     Therefore, the inheritance is remembered, and done dynamically in the doesNotUnderstand
+     implementation."
+
+    self assert:(aClass instSize == 0).
+    inheritedClasses isNil ifTrue:[
+        inheritedClasses := Array with:aClass
+    ] ifFalse:[
+        inheritedClasses := inheritedClasses copyWith:aClass
+    ].
+!
+
 respondTo:aSelector with:aBlock
     "tell the receiver to respond to a message given by selector,
      with evaluating aBlock. The number of arguments as defined by the 
@@ -269,5 +320,5 @@
 !Plug class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libview2/Plug.st,v 1.21 2005-05-04 08:16:17 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libview2/Plug.st,v 1.22 2006-02-14 11:05:34 cg Exp $'
 ! !