Structure.st
author Claus Gittinger <cg@exept.de>
Thu, 15 Aug 1996 15:06:00 +0200
changeset 329 10c2d70e1f61
parent 294 6f937c0ba907
child 343 3232712d4a28
permissions -rw-r--r--
*** empty log message ***

"
 COPYRIGHT (c) 1996 by Claus Gittinger
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

Object subclass:#Structure
	instanceVariableNames:'superclass flags methodDictionary otherSupers instSize i1 i2 i3
		i4 i5 i6 i7 i8 i9 i10 i11 i12 i13 i14 i15 i16 i17 i18 i19 i20'
	classVariableNames:'OneInstance DummyClass ReadAccessMethods WriteAccessMethods
		OtherMethods'
	poolDictionaries:''
	category:'Programming-Support'
!

!Structure  class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1996 by Claus Gittinger
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    are you tired of using arrays or identityDictionaries, when
    multiple values have to be returned from some method, AND
    you dont want to add many stupid dummy data-holder classes to
    avoid the above ?
    (for example, the value as returned by Method>>who) 

    Here is a goody to return an object which is class-less,
    only holding some values, and provides a protocol to access
    those fields. In addition, it supports the array-protocol,
    so it can be used as a backward compatible replacement in
    places where arrays were returned.
    
    For example, in Method>>who, instead of returning:
        ^ Array with:cls with:selector
    you can also return:
        ^ Structure with:#containingClass->cls with:#selector->selector

    and access these values either as:
        retVal at:1     -> returns the cls
        retVal at:2     -> returns the selector
    or (much more convenient and readable) as:
        retVal containingClass
        retVal selector

    implementation note:
        this is a very tricky (but fully legal) implementation,
        creating an objects which is its own class. 
        Therefore, no additional overhead by extra objects is involved.
        
        Another prove that smalltalk is a powerful & flexible programming language.
        However, some smalltalk systems crash if your try this ;-)

    [author:]
        Claus Gittinger

    [see also:]
        Array 
        Behavior
"
!

examples
"
  access is possibly by name:
                                                                [exBegin]
    Transcript showCR:
        (Structure with:#foo->'foo value') foo
                                                                [exEnd]

  AND also by index (for backward compatibility):
                                                                [exBegin]
    Transcript showCR:
        ((Structure with:#foo->'foo value') at:1)
                                                                [exEnd]

  it can be inspected:
                                                                [exBegin]
    (Structure with:#foo->'foo value') inspect
                                                                [exEnd]

  and presents its contents nicely:
                                                                [exBegin]
    (Structure with:#foo->'foo value' with:#bar->'bar value') inspect
                                                                [exEnd]

                                                                [exBegin]
    (Structure with:#foo->'hello' with:#bar->true with:#baz->'world') inspect
                                                                [exEnd]

"
! !

!Structure  class methodsFor:'initialization'!

initialize
    OneInstance isNil ifTrue:[
        OneInstance := self basicNew.

        DummyClass := Behavior shallowCopy.
        DummyClass flags:(Behavior flagBehavior bitOr:Behavior flagPointers).

        ReadAccessMethods := (1 to:20) 
                                collect:[:i | 
                                        (self compiledMethodAt:('i', i printString) asSymbol)
                                ].
        WriteAccessMethods := (1 to:20) 
                                collect:[:i | 
                                        (self compiledMethodAt:('i', i printString,':') asSymbol)
                                ].

        OtherMethods := Array new:6.
        OtherMethods at:1 put:(self compiledMethodAt:#doesNotUnderstand:).
        OtherMethods at:2 put:(Object compiledMethodAt:#class).
        OtherMethods at:3 put:(Object compiledMethodAt:#at:).
        OtherMethods at:4 put:(Object compiledMethodAt:#at:put:).
        OtherMethods at:5 put:(Object compiledMethodAt:#basicAt:).
        OtherMethods at:6 put:(Object compiledMethodAt:#basicAt:put:).
    ].

    "
     OneInstance := nil.
     self initialize.
    "

! !

!Structure  class methodsFor:'instance creation'!

newWith:names
    "return a new structure containing fields as passed in the names collection.
     The argument must be a sequenceable collection of symbols.
     The new structures values are all initialized to nil."

     ^ self newWith:names values:nil

    "
     Structure newWith:#(foo bar)
    "

    "Created: 13.5.1996 / 20:03:42 / cg"
!

newWith:names values:values
    "return a new structure containing fields as passed in the names collection.
     The argument must be a sequenceable collection of symbols.
     The new structures values are set to corresponding values from the second argument, values."

    |cls arr sels mthds dummyClass|

    sels := names collect:[:nm | nm asSymbol].
    sels := sels , (names collect:[:nm | (nm , ':') asSymbol]).
    sels := sels , #(#doesNotUnderstand: #class #at: #at:put: #basicAt: #basicAt:put:).

    mthds := ReadAccessMethods copyTo:names size. 
    mthds := mthds , (WriteAccessMethods copyTo:names size).
    mthds := mthds , OtherMethods.

    "/ create a prototype object as an array ...

    arr := Array new:(names size + 5).
    arr at:1 put:nil.                                                   "/ superclass
    arr at:2 put:(Behavior flagBehavior bitOr:Behavior flagPointers).   "/ flags
    arr at:3 put:(MethodDictionary withKeys:sels andValues:mthds).      "/ selectors & methods
    arr at:4 put:nil.                                                   "/ other supers
    arr at:5 put:5.                                                     "/ instSize 

    "/ now, the big trick ...

    arr changeClassTo:DummyClass.
    arr changeClassTo:arr.

    values notNil ifTrue:[
        values keysAndValuesDo:[:i :val |
            arr at:i put:val
        ]
    ].

    ^ arr.  

    "
     Structure newWith:#(foo bar) values:#('foo' 'bar')
    "

    "Created: 13.5.1996 / 20:03:42 / cg"
    "Modified: 3.7.1996 / 10:24:43 / cg"
!

with:assoc
    "return a new structure with a single field, named to the assocs key,
     and initialized with assocs value."

     ^ self newWith:(Array with:assoc key) values:(Array with:assoc value)

    "
     Structure with:#foo->'foo'
    "
!

with:assoc1 with:assoc2
    "return a new structure with two fields, named as defined by the arguments'
     keys, and and initialized with the assocs' values."

     ^ self newWith:(Array with:assoc1 key with:assoc2 key) 
             values:(Array with:assoc1 value with:assoc2 value)

    "
     Structure with:#foo->'foo' with:#bar->'bar'
    "
!

with:assoc1 with:assoc2 with:assoc3
    "return a new structure with three fields, named as defined by the arguments'
     keys, and and initialized with the assocs' values."

     ^ self newWith:(Array with:assoc1 key with:assoc2 key with:assoc3 key) 
             values:(Array with:assoc1 value with:assoc2 value with:assoc3 value)

    "
     Structure with:#foo->'foo' with:#bar->'bar' with:#baz->'baz'
    "
!

with:assoc1 with:assoc2 with:assoc3 with:assoc4
    "return a new structure with four fields, named as defined by the arguments'
     keys, and and initialized with the assocs' values."

     ^ self newWith:(Array with:assoc1 key with:assoc2 key 
                           with:assoc3 key with:assoc4 key) 
             values:(Array with:assoc1 value with:assoc2 value 
                           with:assoc3 value with:assoc4 value)

    "
     Structure with:#foo->'foo' with:#bar->'bar' with:#baz->'baz' with:#hello->'hello'
    "
!

with:assoc1 with:assoc2 with:assoc3 with:assoc4 with:assoc5
    "return a new structure with five fields, named as defined by the arguments'
     keys, and and initialized with the assocs' values."

     ^ self newWith:(Array with:assoc1 key with:assoc2 key 
                           with:assoc3 key with:assoc4 key      
                           with:assoc5 key) 
             values:(Array with:assoc1 value with:assoc2 value 
                           with:assoc3 value with:assoc4 value 
                           with:assoc5 value)

    "
     Structure with:#foo->'foo' with:#bar->'bar' with:#baz->'baz' with:#hello->'hello' with:#world->'world'
    "
! !

!Structure methodsFor:'accessing'!

flags
    "return the flags - required class protocol"

    ^ flags

    "Created: 13.5.1996 / 21:19:23 / cg"
!

flags:something
    "set the flags - required class protocol"

    flags := something.

    "Created: 13.5.1996 / 21:19:23 / cg"
!

i1
    "return the first instance variable"

    ^ i1

    "Created: 13.5.1996 / 21:19:25 / cg"
!

i10
    "return i10"

    ^ i10

    "Created: 13.5.1996 / 21:19:27 / cg"
!

i10:something
    "set i10"

    i10 := something.

    "Created: 13.5.1996 / 21:19:27 / cg"
!

i11
    "return i11"

    ^ i11

    "Created: 13.5.1996 / 21:19:27 / cg"
!

i11:something
    "set i11"

    i11 := something.

    "Created: 13.5.1996 / 21:19:27 / cg"
!

i12
    "return i12"

    ^ i12

    "Created: 13.5.1996 / 21:19:27 / cg"
!

i12:something
    "set i12"

    i12 := something.

    "Created: 13.5.1996 / 21:19:27 / cg"
!

i13
    "return i13"

    ^ i13

    "Created: 13.5.1996 / 21:19:28 / cg"
!

i13:something
    "set i13"

    i13 := something.

    "Created: 13.5.1996 / 21:19:28 / cg"
!

i14
    "return i14"

    ^ i14

    "Created: 13.5.1996 / 21:19:28 / cg"
!

i14:something
    "set i14"

    i14 := something.

    "Created: 13.5.1996 / 21:19:28 / cg"
!

i15
    "return i15"

    ^ i15

    "Created: 13.5.1996 / 21:19:28 / cg"
!

i15:something
    "set i15"

    i15 := something.

    "Created: 13.5.1996 / 21:19:28 / cg"
!

i16
    "return i16"

    ^ i16

    "Created: 13.5.1996 / 21:19:28 / cg"
!

i16:something
    "set i16"

    i16 := something.

    "Created: 13.5.1996 / 21:19:28 / cg"
!

i17
    "return i17"

    ^ i17

    "Created: 13.5.1996 / 21:19:28 / cg"
!

i17:something
    "set i17"

    i17 := something.

    "Created: 13.5.1996 / 21:19:29 / cg"
!

i18
    "return i18"

    ^ i18

    "Created: 13.5.1996 / 21:19:29 / cg"
!

i18:something
    "set i18"

    i18 := something.

    "Created: 13.5.1996 / 21:19:29 / cg"
!

i19
    "return i19"

    ^ i19

    "Created: 13.5.1996 / 21:19:29 / cg"
!

i19:something
    "set i19"

    i19 := something.

    "Created: 13.5.1996 / 21:19:29 / cg"
!

i1:something
    "set i1"

    i1 := something.

    "Created: 13.5.1996 / 21:19:25 / cg"
!

i2
    "return i2"

    ^ i2

    "Created: 13.5.1996 / 21:19:25 / cg"
!

i20
    "return i20"

    ^ i20

    "Created: 13.5.1996 / 21:19:29 / cg"
!

i20:something
    "set i20"

    i20 := something.

    "Created: 13.5.1996 / 21:19:29 / cg"
!

i2:something
    "set i2"

    i2 := something.

    "Created: 13.5.1996 / 21:19:25 / cg"
!

i3
    "return i3"

    ^ i3

    "Created: 13.5.1996 / 21:19:25 / cg"
!

i3:something
    "set i3"

    i3 := something.

    "Created: 13.5.1996 / 21:19:25 / cg"
!

i4
    "return i4"

    ^ i4

    "Created: 13.5.1996 / 21:19:25 / cg"
!

i4:something
    "set i4"

    i4 := something.

    "Created: 13.5.1996 / 21:19:25 / cg"
!

i5
    "return i5"

    ^ i5

    "Created: 13.5.1996 / 21:19:26 / cg"
!

i5:something
    "set i5"

    i5 := something.

    "Created: 13.5.1996 / 21:19:26 / cg"
!

i6
    "return i6"

    ^ i6

    "Created: 13.5.1996 / 21:19:26 / cg"
!

i6:something
    "set i6"

    i6 := something.

    "Created: 13.5.1996 / 21:19:26 / cg"
!

i7
    "return i7"

    ^ i7

    "Created: 13.5.1996 / 21:19:26 / cg"
!

i7:something
    "set i7"

    i7 := something.

    "Created: 13.5.1996 / 21:19:26 / cg"
!

i8
    "return i8"

    ^ i8

    "Created: 13.5.1996 / 21:19:26 / cg"
!

i8:something
    "set i8"

    i8 := something.

    "Created: 13.5.1996 / 21:19:26 / cg"
!

i9
    "return i9"

    ^ i9

    "Created: 13.5.1996 / 21:19:26 / cg"
!

i9:something
    "set i9"

    i9 := something.

    "Created: 13.5.1996 / 21:19:27 / cg"
!

instSize
    "return instSize - required class protocol"

    ^ instSize

    "Created: 13.5.1996 / 21:19:24 / cg"
!

instSize:something
    "set instSize - required class protocol"

    instSize := something.

    "Created: 13.5.1996 / 21:19:25 / cg"
!

methodArray
    "return methodArray - required class protocol"

    ^ methodArray

    "Created: 13.5.1996 / 21:19:24 / cg"
!

methodArray:something
    "set methodArray - required class protocol"

    methodArray := something.

    "Created: 13.5.1996 / 21:19:24 / cg"
!

otherSupers
    "return otherSupers"

    ^ otherSupers

    "Created: 13.5.1996 / 21:19:24 / cg"
!

otherSupers:something
    "set otherSupers"

    otherSupers := something.

    "Created: 13.5.1996 / 21:19:24 / cg"
!

selectorArray
    "return selectorArray - required class protocol"

    ^ selectorArray

    "Created: 13.5.1996 / 21:19:24 / cg"
!

selectorArray:something
    "set selectorArray - required class protocol"

    selectorArray := something.

    "Created: 13.5.1996 / 21:19:24 / cg"
!

superclass
    "return superclass - required class protocol"

    ^ superclass

    "Created: 13.5.1996 / 21:19:23 / cg"
!

superclass:something
    "set superclass - required class protocol"

    superclass := something.

    "Created: 13.5.1996 / 21:19:23 / cg"
! !

!Structure methodsFor:'stubs'!

doesNotUnderstand:aMessage
    "catch unimplemented messages - pass some to the superclass.
     Notice that although this method calls super messages,
     actual instances will have no valid superClass."

    |sel args names sz s|

    "/ instance protocol

    sel := aMessage selector.
    args := aMessage arguments.

    sel == #displayString ifTrue:[
       s := WriteStream on:''.
       s nextPutAll:'Structure('.
        names := self allInstVarNames.
        names keysAndValuesDo:[:idx :nm |
            s nextPutAll:nm; nextPutAll:'->'.
            s nextPutAll:(self at:idx) displayString.
            s space
        ].
        s nextPutAll:')'.
        ^ s contents
    ].

    sel == #printString ifTrue:[
         ^ super printString
    ].

    sel == #printOn: ifTrue:[
         ^ super printOn:(args at:1)
    ].

    sel == #basicInspect ifTrue:[
        ^ InspectorView openOn:self
    ].

    sel == #inspect ifTrue:[
        ^ InspectorView openOn:self
    ].

    sel == #instVarAt: ifTrue:[
        |nr|

        nr := args at:1.
        nr == 1 ifTrue:[^ i1].
        nr == 2 ifTrue:[^ i2].
        nr == 3 ifTrue:[^ i3].
        nr == 4 ifTrue:[^ i4].
        nr == 5 ifTrue:[^ i5].
        nr == 6 ifTrue:[^ i6].
        ^ nil
    ].

    (sel == #size
    or:[sel == #basicSize]) ifTrue:[
         ^ super basicSize
    ].

    (sel == #at:
    or:[sel == #basicAt:]) ifTrue:[
         ^ super basicAt:(args at:1)
    ].

    (sel == #at:put:
    or:[sel == #basicAt:put:]) ifTrue:[
         ^ super basicAt:(args at:1) put:(args at:2)
    ].

    sel == #== ifTrue:[
         ^ self == (args at:1)
    ].

    "/ class protocol

    (sel := aMessage selector) == #name ifTrue:[
        ^ 'Structure'
    ].

    sel == #instSize ifTrue:[
        ^ instSize
    ].

    sel == #isVariable ifTrue:[
         ^ false
    ].

    sel == #isClass ifTrue:[
         ^ false
    ].

    sel == #isMeta ifTrue:[
         ^ false
    ].

    sel == #isBehavior ifTrue:[
         ^ false
    ].

    sel == #respondsTo: ifTrue:[
        (args at:1) printNL.
         ^ false
    ].

    sel == #evaluatorClass ifTrue:[
        ^ Compiler
    ].

    sel == #classNameWithArticle ifTrue:[
         ^ self displayString
    ].

    sel == #allSubclasses ifTrue:[
        ^ #()
    ].

    sel == #allClassVarNames ifTrue:[
        ^ #()
    ].

    sel == #allInstVarNames ifTrue:[
        sz := super basicSize.
        names := Array new:sz.

        methodDictionary copy keysAndValuesDo:[:sel :mthd|
            |index|
            (sel endsWith:$:) ifFalse:[
                (sel ~~ #class) ifTrue:[
                    "/
                    "/ which method is it ?
                    "/
                    (1 to:20) do:[:i |
                        |mysel|

                        mysel := ('i' , i printString) asSymbol.
                        mthd == (Structure compiledMethodAt:mysel) ifTrue:[
                            index := i
                        ]
                    ].

                    index isNil ifTrue:[
                        'oops' printNL.
                        ^ nil
                    ].

                    names at:index put:sel.                
                ]
            ]
        ].
        "/ must now sort by index ...
        
         ^ names
    ].

    aMessage printNL.
    'args ' print. args printNL.

    ^ nil.

    "Created: 13.5.1996 / 20:22:22 / cg"
    "Modified: 13.5.1996 / 21:12:54 / cg"
! !

!Structure  class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libcomp/Structure.st,v 1.3 1996-07-03 09:01:11 cg Exp $'
! !
Structure initialize!