home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2004 #2 / Amiga Plus CD - 2004 - No. 02.iso / AmigaPlus / Tools / Development / AmigaTalk / general / ProtocolAdaptor.st < prev    next >
Encoding:
Text File  |  2004-01-31  |  16.0 KB  |  439 lines

  1. " -------------------------------------------------------------------
  2.   Class ProtocolAdaptor is an abstract class that introduces the 
  3.   concept of a ValueModel which redirects the value and value: 
  4.   messages to another object (the subject) and adds lazy dependency.  
  5.   Lazy dependency means that the ProtocolAdaptor will only register 
  6.   itself as a dependent of the subject if it has at least one dependent 
  7.   and the subject will send update messages.  Subclasses which implement 
  8.   value: are responsible for sending update messages if the subject 
  9.   does not send update messages. 
  10.  
  11.   ProtocolAdaptors can have a collection of object used to transform 
  12.   the subject into the 'target' which subclasses then operate on.
  13.  
  14.   A ProtocolAdaptor has either a constant or variable subject.  The 
  15.   constant subject is initialized with the subject: or 
  16.   subject:sendsUpdates: messages.  A variable subject is commonly used 
  17.   when a set of adaptors all adapt a different part of the same subject 
  18.   or the subject is changed after initialization.  A variable subject 
  19.   requires the use of a ValueModel to inform the ProtocolAdaptor of the 
  20.   new subject and is initialized using the subjectChannel: or 
  21.   subjectChannel:sendsUpdates: messages.
  22.  
  23.   Dependents are notified of the value changing when the subject is changed.
  24.  
  25.     Instance Variables:
  26.       subject              <Object> The object we're adapting.
  27.       subjectSendsUpdates  <Boolean> When this is set to true, it is 
  28.                                      assumed that the subject will 
  29.         send update notices and we'll pass them on to our dependents 
  30.         when received.  When set to false, the adaptor generates the 
  31.         update notice to the dependents of the adaptor.  The lazy dependency
  32.         mechanism avoids double-notificaton of dependents when the subject 
  33.         does send updates.
  34.    
  35.       subjectChannel       <ValueModel> When this sends a change notice, 
  36.                                         update the subject.
  37.  
  38.       accessPath    <SequencableCollection | nil> holds accessors to turn 
  39.                                              the subject into the target
  40.  
  41.  
  42.   Object Reference:
  43.   ProtocolAdaptor is an abstract class that provides its subclasses with 
  44.   the ability to get and set an embedded value in an object other than 
  45.   the application model, such as an instance variable in a domain model. 
  46.   Each such adaptor has a subject, which is typically a composite domain 
  47.   model, and specialized value-getting and -setting messages for extracting 
  48.   the desired value from the subject. 
  49.   For example, suppose you are creating a canvas containing one input 
  50.   field for each part of a Customer object: accountNumber, name, company, 
  51.   address, and so on.  Since the accountNumber is held by a Customer 
  52.   object rather than by the application model, an ordinary ValueHolder 
  53.   offers no help in accessing it.  While you can create a duplicate 
  54.   accountNumber variable in the application model, and charge the 
  55.   application model with the responsibility of updating the Customer 
  56.   object whenever the input field is changed, this is cumbersome, 
  57.   especially for a large number of such fields.  A ProtocolAdaptor (in this 
  58.   case, an AspectAdaptor) enables you to cut out the middle man by 
  59.   getting and setting the accountNumber in the Customer object directly. 
  60.   In this case, the Customer would be the subject of several 
  61.   AspectAdaptors -- one adaptor translates #value and #value: into 
  62.   #accountNumber and #accountNumber:, another adaptor manages the 
  63.   customer name, and so on. 
  64.   The subject can be changed during the life of an adaptor -- for 
  65.   example, a new instance of Customer can become the focus of the adaptor's 
  66.   inquiries.  When a change of subject is likely, it is most economical 
  67.   to first enfold the subject in a value holder.  This value holder is 
  68.   known as a subject channel, because it provides a channel to the subject. 
  69.   In that case, the adaptor would be created via a #subjectChannel: 
  70.   message rather than a #subject: message.  In the example, instead of 
  71.   storing a Customer object in an instance variable of the application 
  72.   model, we would store a value holder containing the Customer object. 
  73.   Because both the adaptor and its subject are capable of sending 
  74.   #update:with:from: messages to the same dependent, it is sometimes 
  75.   necessary to disable the adaptor's update facility.  This is usually 
  76.   done at instance creation time, via a #subjectSendsUpdates: message. 
  77.   By default, the adaptor assumes that the subject does not send redundant 
  78.   update messages. 
  79.   ProtocolAdaptor is actually capable of extracting a value that is 
  80.   deeply embedded in the subject.  For example, suppose the Customer 
  81.   holds an AccountNumber object, which holds an AccountPrefix object, 
  82.   which holds a prefixCharacter and a prefixNumber.  The AspectAdaptor 
  83.   for the prefixNumber would need to send #accountNumber to the address, 
  84.   then send #accountPrefix to the account number.  This series of messages 
  85.   is called the access path, and is initialized via #accessPath:. 
  86.   AspectAdaptor is the most commonly used subclass of ProtocolAdaptor. 
  87.   IndexedAdaptor is used to access an element in a collection. 
  88.   When creating a subclass, equip it with the following methods: 
  89.  
  90.      Instance protocol 
  91.        setValueUsingTarget:to: 
  92.        valueUsingTarget: 
  93.  
  94.   A subclass that implements #value: is responsible for sending an 
  95.   update message if its subject does not send one. 
  96.   ------------------------------------------------------------------------
  97. "
  98.  
  99. Class ProtocolAdaptor :ValueModel
  100. ! subject subjectSendsUpdates subjectChannel accessPath !
  101. [
  102.    dependents
  103.      ^ super dependents
  104. |     
  105.    accessPath: aSequenceableCollection
  106.      " Answer a new instance of the receiver with accessPath
  107.      * aSequenceableCollection.
  108.      "
  109.      ^ self new setAccessPath: aSequenceableCollection
  110. |
  111.    new
  112.      ^ super new initialize
  113. |
  114.    subject: aSubject
  115.      " Answer a new ProtocolAdaptor with a constant subject 
  116.      * (aSubject).  By default, the ProtocolAdaptor's subject 
  117.      * does not send update notices to its dependents.
  118.      * Note:  For a ProtocolAdaptor which will change subjects
  119.      * frequently, or a group of ProtocolAdaptors which should 
  120.      * all share a subject and change at the same time, 
  121.      * subjectChannel: provides a convenient interface.
  122.      "
  123.      ^ self subject: aSubject sendsUpdates: false
  124. |
  125.    subject: aSubject accessPath: aSequenceableCollection
  126.      " Create and initialize the ProtocolAdaptor. "
  127.  
  128.      ^ (self accessPath: aSequenceableCollection) subject: aSubject
  129. |
  130.    subject: aSubject sendsUpdates: aBoolean
  131.      " Answer a new ProtocolAdaptor with a constant subject 
  132.      * (aSubject).  This ProtocolAdaptor will send update 
  133.      * messages when the value changes if aBoolean is false.
  134.      * Note:  For a ProtocolAdaptor which will change subjects
  135.      * frequently, or a group of ProtocolAdaptors which should 
  136.      * all share a subject and change at the same time, 
  137.      * subjectChannel:sendsUpdates: provides a convenient interface.
  138.      "
  139.      ^ (self new) setASubject: aSubject; subjectSendsUpdates: aBoolean
  140. |
  141.    subject: aSubject sendsUpdates: aBoolean 
  142.                        accessPath: aSequencableCollection
  143.      " Create and initialize the ProtocolAdaptor. "
  144.  
  145.      ^ (self accessPath: aSequencableCollection) 
  146.                 subject: aSubject;
  147.              subjectSendsUpdates: aBoolean
  148. |
  149.    subjectChannel: aValueModel
  150.      " Answer a new ProtocolAdaptor with a variable subject 
  151.      * (aValueModel is the subject channel) to notify it of 
  152.      * changes in the subject.  By default, the   ProtocolAdaptor's 
  153.      * subject does not send update notices to its dependents.
  154.      * Note:  A ProtocolAdaptor which will not change subjects
  155.      * does not need to   use a subject channel.  It is more 
  156.      * efficient and convenient to set the subject using subject:
  157.      "
  158.      ^ self subjectChannel: aValueModel sendsUpdates: false
  159. |
  160.    subjectChannel: aValueModel accessPath: aSequenceableCollection
  161.  
  162.      ^ (self accessPath: aSequenceableCollection) subjectChannel: aValueModel
  163. |
  164.    subjectChannel: aValueModel sendsUpdates: aBoolean
  165.      " Answer a new ProtocolAdaptor with a variable subject
  166.      * (aValueModel is the subject channel) to notify it of 
  167.      * changes in the subject.  The ProtocolAdaptor will send 
  168.      * update messages when the value changes if aBoolean is false.
  169.      * Note:  A ProtocolAdaptor which will not change subjects 
  170.      * does not need to use a subject channel.  It is more 
  171.      * efficient and convenient to set the subject
  172.      * using subject:sendsUpdates:
  173.      "
  174.      ^ (self new) subjectSendsUpdates: aBoolean; subjectChannel: aValueModel
  175. |
  176.    subjectChannel: aValueModel sendsUpdates: aBoolean 
  177.        accessPath: aSequenceableCollection
  178.  
  179.      ^ (self accessPath: aSequenceableCollection)
  180.              subjectSendsUpdates: aBoolean;
  181.              subjectChannel: aValueModel
  182. |
  183.    initialize
  184.  
  185.      super initialize.
  186.  
  187.      subjectSendsUpdates <- false
  188. |
  189.    releaseParts
  190.      " Remove the receiver as a dependent of the receiver's subject. "
  191.  
  192.      (subject notNil and: [super dependents notNil])
  193.          ifTrue:   [ self unhookFromSubject ].
  194.    
  195.      (subjectChannel notNil)
  196.          ifTrue:   [subjectChannel removeDependent: self].
  197. |
  198.    subject
  199.      " Answer the current subject. "
  200.  
  201.      ^ subject
  202. |
  203.    setASubject: anObject ! sc !
  204.      " Set the subject to be anObject.  Send an update since the value has
  205.      * probably changed too.  If this ProtocolAdaptor has a subject channel,
  206.      * delegate setting the new subject to it so that others depending 
  207.      * on the same subject channel value model will be informed automatically.
  208.      * Note:  For a ProtocolAdaptor which will change subjects frequently,
  209.      * or a group of ProtocolAdaptors which should all share a subject and
  210.      * change at the same time, subjectChannel: provides a convenient interface.
  211.      "
  212.      ((sc <- self subjectChannel) notNil)
  213.          ifTrue:   [ sc value: anObject ]
  214.         ifFalse:   [ self setSubject: anObject ]
  215. |
  216.    setSubject: anObject
  217.      " Set the subject to be anObject.  Send an update since the
  218.      * value has probably changed too.
  219.      "
  220.  
  221.      (subject notNil and: [super dependents notNil])
  222.         ifTrue:   [ self unhookFromSubject ].
  223.  
  224.      subject <- anObject.
  225.  
  226.      (subject notNil and: [super dependents notNil])
  227.         ifTrue:   [ self hookupToSubject ].
  228.  
  229.      super dependents update: #value with: nil from: self
  230. |
  231.    subjectChannel
  232.      " Answer the ValueModel used to provide new subjects."
  233.  
  234.      ^ subjectChannel
  235. |
  236.    setSubjectChannel: aValueModel
  237.      " Set or change the ValueModel we depend on to provide the latest subject.
  238.      * In the rare cases where the subject channel needs to be reinitialized an
  239.      * update message is sent on the assumption that the value has changed.
  240.      "
  241.      (subjectChannel notNil)
  242.         ifTrue:   [ subjectChannel removeDependent: self ].
  243.  
  244.      subjectChannel <- aValueModel.
  245.  
  246.      (subjectChannel notNil)
  247.         ifTrue:   [ subjectChannel addDependent: self ].
  248.  
  249.      self changedSubject
  250. |
  251.    subjectSendsUpdates
  252.      " Does our subject send updates to its dependents?"
  253.  
  254.      ^ subjectSendsUpdates
  255. |
  256.    subjectSendsUpdates: aBoolean
  257.      " Set or change the nature of the subject.
  258.      * If the subject does not send updates, we 
  259.      * won't bother to depend on it.
  260.      "
  261.      (subject notNil and: [super dependents notNil])
  262.         ifTrue:   [ self unhookFromSubject ].
  263.  
  264.      subjectSendsUpdates <- aBoolean.
  265.  
  266.      (subject notNil and: [super dependents notNil])
  267.         ifTrue:   [ self hookupToSubject ]
  268. |
  269.    accessPath
  270.      " Answer the receiver's accessPath.  This is a collection
  271.      * of accessors used to turn the receiver's subject into 
  272.      * the target for messages.
  273.      "
  274.      ^ accessPath
  275. |
  276.    setAccessPath: aSequenceableCollection
  277.      " Set the receiver's accessPath to be aSequenceableCollection.
  278.      * This will be used to turn the subject into the target.
  279.      "
  280.  
  281.      accessPath <- aSequenceableCollection.
  282. |
  283.    setValue: newValue
  284.      " Set a new value using the reciever's target."
  285.  
  286.      self setValueUsingTarget: self target to: newValue
  287. |
  288.    target
  289.      " Answer the receiver's target for operations.
  290.      * If there is an accessPath it will hold accessors that 
  291.      * will be used to turn the subject into the target.
  292.      "
  293.  
  294.      ^ self targetUsingSubject: subject
  295. |
  296.    value
  297.      " Answer the value returned by sending the receiver's 
  298.      * retrieval (get) selector to the receiver's target.
  299.      "
  300.  
  301.      ^ self valueUsingSubject: subject
  302. |
  303.    value: newValue
  304.      " Set the currently stored value, and notify dependents. "
  305.  
  306.      self setValue: newValue.
  307.     
  308.      (subjectSendsUpdates = true)
  309.         ifFalse: [self changed: #value ]
  310. |
  311.    valueUsingSubject: aSubject
  312.      " Answer a value for the subject if aSubject were the receiver's subject."
  313.  
  314.      ^ self valueUsingTarget: (self targetUsingSubject: aSubject)
  315. |
  316.    addDependent: anObject
  317.      " Add anObject as one of the receiver's dependents."
  318.  
  319.      (super dependents == nil)
  320.         ifTrue: [self hookupToSubject].
  321.   
  322.      ^ super addDependent: anObject
  323. |
  324.    removeDependent: anObject
  325.      " Remove the argument, anObject, as one of the receiver's dependents."
  326.  
  327.      super removeDependent: anObject.
  328.   
  329.      (super dependents == nil)
  330.         ifTrue: [self unhookFromSubject].
  331.    
  332.      ^ anObject
  333. |
  334.    update: anAspect with: parameters from: anObject
  335.      " If the update is from the subjectChannel, it must be because
  336.      * there is a new subject.
  337.      "
  338.      (anObject == subjectChannel)
  339.         ifTrue: [self changedSubject]
  340. |
  341.    isProtocolAdaptor
  342.      " Answer as to whether the receiver transduces protocol
  343.      * into ValueModel protocol.
  344.      "
  345.      ^ true
  346. |
  347.    printOn: aStream
  348.  
  349.      super printOn: aStream.
  350.      
  351.      aStream nextPut: $(.
  352.      
  353.      self target printOn: aStream.
  354.      
  355.      aStream nextPut: $).
  356. |
  357.    printPathOn: aStream ! path !
  358.  
  359.      ((path <- self accessPath) notNil and: [path isEmpty not])
  360.         ifTrue: [path do: [:elt | (elt isSymbol)
  361.                                     ifTrue: [aStream nextPutAll: elt]
  362.                                    ifFalse: [elt printOn: aStream].
  363.  
  364.                             aStream space ] ].
  365. |
  366.    access: anObject with: anAccessor
  367.  
  368.      ^ (anAccessor isSymbol)
  369.          ifTrue:  [anObject perform: anAccessor]
  370.          ifFalse: [(anAccessor isInteger)
  371.                      ifTrue: [anObject at: anAccessor]
  372.                      ifFalse: [anAccessor value: anObject ] ]
  373. |
  374.    changedSubject
  375.      " The subject has changed. "
  376.  
  377.      self setSubject: self subjectChannel value
  378. |
  379.    hookupToSubject
  380.      " Add the receiver as a dependent of the receiver's subject."
  381.  
  382.      (subjectSendsUpdates)
  383.         ifTrue: [(subject notNil)
  384.                    ifTrue: [subject addDependent: self]]
  385. |
  386.    makeAdaptorForRenderingStoreLeafInto: pair
  387.  
  388.      pair at: 1 put: self.
  389.      self subjectChannel: nil.
  390.    
  391.      ^ (subject isProtocolAdaptor)
  392.          ifTrue: [subject <- subject copy.
  393.                   subject makeAdaptorForRenderingStoreLeafInto: pair]
  394.         ifFalse: [pair]
  395. |
  396.    renderingValueUsingSubject: aSubject ! pair cpy cell !
  397.  
  398.      (subject isProtocolAdaptor)
  399.         ifFalse: [^self valueUsingSubject: aSubject].
  400.  
  401.      cpy  <- self copy.
  402.      pair <- Array new: 2.
  403.  
  404.      pair at: 2 put: cpy.
  405.  
  406.      cell <- cpy makeAdaptorForRenderingStoreLeafInto: pair.
  407.  
  408.      cell first subjectChannel: aSubject asValue.
  409.  
  410.      ^ cell last value
  411. |
  412.    setValueUsingTarget: anObject to: newValue
  413.      "Using anObject set a new value."
  414.   
  415.      ^ self subclassResponsibility: 'setValueUsingTarget:to:'
  416. |
  417.    targetUsingSubject: aSubject ! obj path !
  418.  
  419.      obj <- aSubject.
  420.      path <- self accessPath.
  421.  
  422.      1 to: path size do: [:i | (obj == nil)
  423.                                  ifTrue: [^nil].
  424.  
  425.                            obj <- self access: obj with: (path at: i)].
  426.      ^ obj
  427. |
  428.    unhookFromSubject
  429.      " Remove the receiver as a dependent of the receiver's subject. "
  430.  
  431.      (subjectSendsUpdates)
  432.         ifTrue: [subject removeDependent: self]
  433. |
  434.    valueUsingTarget: anObject   
  435.      " Answer the value returned by using anObject. "
  436.  
  437.      ^ self subclassResponsibility: 'valueUsingTarget:'
  438. ]
  439.