ClassDefinitionChange.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Wed, 01 May 2013 09:34:36 +0100
branchjv
changeset 3243 292f55bcd8f0
parent 3224 c42523c55cad
child 3283 65d968484661
permissions -rw-r--r--
Fixes in ChangeSet::ClassSourceWriter. Fixes test RegressionTests::ChangeSetTests>>test_ClassSourceWriter_01

"
 COPYRIGHT (c) 1993 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.
"
"{ Package: 'stx:libbasic3' }"

ClassChange subclass:#ClassDefinitionChange
	instanceVariableNames:'objectType superClassName classType indexedType otherParameters
		instanceVariableNames classVariableNames
		classInstanceVariableNames poolDictionaries category private
		definitionSelector owningClassName'
	classVariableNames:''
	poolDictionaries:''
	category:'System-Changes'
!

!ClassDefinitionChange class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1993 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
"
    instances represent class definition-changes. 
    They are typically held in a ChangeSet.

    [author:]
        Claus Gittinger
"
! !

!ClassDefinitionChange methodsFor:'accessing'!

basicSuperClassName 
    ^ superClassName
!

category
    category isNil ifTrue:[
        self setupFromSource.
    ].
    ^ category

    "Modified: / 11-10-2006 / 14:12:01 / cg"
!

category:something
    category := something.
!

classInstanceVariableNames
    ^ classInstanceVariableNames
!

classInstanceVariableNames:something
    classInstanceVariableNames := something.
!

classVariableNames
    ^ classVariableNames
!

classVariableNames:something
    classVariableNames := something.
!

delta
    | class |

    class := self changeClass.
    class ifNil:[^#+].

    ^(self class isSource: self source sameSourceAs: class definitionWithoutPackage "definition")
        ifTrue:[#=]
        ifFalse:[#~]

    "Modified: / 31-08-2011 / 09:26:48 / cg"
!

deltaDetail
    "Returns a delta to the current state as a ChangeDelta object"

    | class mySource imageSource myTree imageTree same |

    class := self changeClass.
    class isNil ifTrue:[^ ChangeDeltaInformation added ].
    class isLoaded ifFalse:[^ ChangeDeltaInformation different ].
    mySource := self source.
    imageSource := class definitionWithoutPackage "definition".
    same := (mySource = imageSource).
    same ifFalse:[
        same := (self class isSource: mySource sameSourceAs: imageSource ).
        same ifFalse:[
            "/ care for formatting (tabs, indentation etc.)
            myTree := RBParser parseExpression:mySource.
            imageTree := RBParser parseExpression:imageSource.
            same := (myTree = imageTree).
            same ifFalse:[
                "/ some classDefinition strings contain sn
                "/ instVarName string like 'foo bar ' instead of 'foo bar' (i.e. added a space)...
                ((myTree receiver = imageTree receiver)
                    and:[ (myTree selector = imageTree selector)
                    and:[ ('*ubclass:instanceVariableNames:classVariableNames:poolDictionaries:category:' match: myTree selector)
                    and:[ (myTree arguments at:1) = (imageTree arguments at:1)  

                    and:[ (2 to:5) conform:[:i |
                            ((myTree arguments at:i) isLiteral
                            and:[ (imageTree arguments at:i) isLiteral
                            and:[ (myTree arguments at:i) value asString withoutSeparators
                                  = (imageTree arguments at:i) value asString withoutSeparators ]]) ]   

                ]]]])
                    ifTrue:[
                        same := true
                    ]
            ].
        ]
    ].

    ^ same 
        ifTrue:[ ChangeDeltaInformation identical ]
        ifFalse:[ ChangeDeltaInformation different ]

    "Created: / 31-08-2011 / 10:26:42 / cg"
    "Modified: / 24-01-2012 / 22:13:23 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

imageSource
    "return the source for the in-image version of the method"

    | cls |

    cls := self changeClass.
    cls isNil ifTrue:[ ^ nil ].
    ^ cls definitionWithoutPackage
!

instanceVariableNames
    ^ instanceVariableNames
!

instanceVariableNames:something
    instanceVariableNames := something.
!

localClassName
    "for private classes, this returns the name relative to its owner;
     for non-private ones, this is the regular name.
     Notice that className always returns the full name (incl. any owner prefix)"

    self isPrivateClassDefinitionChange ifFalse:[^ self className].

    (className startsWith:(owningClassName,'::')) ifTrue:[
        ^ className copyFrom:(owningClassName size + 2 + 1).
    ] ifFalse:[
        "/ should not happen
        ^ self className
    ]
!

nameSpaceName
    objectType == #variable ifTrue:[
        ^ nil
    ].
    ^ self cutNameSpaceOf:(self nameSpaceOverride ? super nameSpaceName)

    "Modified: / 07-09-2011 / 20:47:14 / cg"
!

nameSpaceName: aNameSpaceName classType: aClassType otherParameters:otherParametersArg
    "this instance setup message is used when reading from a VW-xml change file.
     Support for this is not yet complete."

    |indexedType imports|

    nameSpaceOverride := aNameSpaceName.
    classType := aClassType.
    otherParameters := Dictionary new addAll:otherParametersArg; yourself.

    superClassName := otherParameters at:#superclass: ifAbsent:nil.
    self assert:(superClassName notNil).
    superClassName notNil ifTrue:[
        superClassName := superClassName pathString.
    ].
    indexedType := otherParameters at:#indexedType: ifAbsent:nil.
    private := otherParameters at:#private: ifAbsent:nil.
    instanceVariableNames := otherParameters at:#instanceVariableNames: ifAbsent:nil.
    classInstanceVariableNames := otherParameters at:#classInstanceVariableNames: ifAbsent:nil.
    imports := otherParameters at:#imports: ifAbsent:nil.
    category := otherParameters at:#category: ifAbsent:nil.

    "Modified: / 15-06-2010 / 14:50:35 / cg"
!

objectType
    "return the value of the instance variable 'objectType' (automatically generated)"

    ^ objectType
!

objectType:something
    "set the value of the instance variable 'objectType' (automatically generated)"

    objectType := something.
!

poolDictionaries
    poolDictionaries isNil ifTrue:[
        self setupFromSource.
    ].
    ^ poolDictionaries

    "Modified: / 13-06-2012 / 12:23:41 / cg"
!

poolDictionaries:something
    poolDictionaries := something.
!

private:aBoolean
    private := aBoolean.

    "Created: / 30-08-2010 / 13:56:11 / cg"
!

source
    "return the source of the change;
     either the original source or a synthesized"

    |src|

    (src := source) isNil ifTrue:[
        src := self definitionString
    ].
    ^ src

    "Modified: / 10-08-2012 / 11:53:54 / cg"
!

superClassName 
    |nm|

    nm := superClassName.
    nm isNil ifTrue:[^ 'nil'].
    "/ convert VW namespace syntax
    (nm includes:$.) ifTrue:[
        ^ nm copyReplaceAll:$. withAll:'::'.
    ].
    ^ nm
!

superClassName:aString
    superClassName := aString.
    self assert:(aString notNil).
    self invalidateSource.
!

superClassNameWithoutMyNamespace
    |nm|

    superClassName isNil ifTrue:[^ 'nil'].

    nm := self cutMyNameSpaceOf:superClassName.
    (nm includes:$.) ifTrue:[
        ^ nm copyReplaceAll:$. withAll:'::'.
    ].
    ^ nm

    "Modified: / 03-03-2007 / 13:09:07 / cg"
!

superClassNameWithoutNamespace
    |nm|

    nm := self cutNameSpaceOf:superClassName.
    (nm includes:$.) ifTrue:[
        ^ nm copyReplaceAll:$. withAll:'::'.
    ].
    ^ nm
! !

!ClassDefinitionChange methodsFor:'applying'!

apply
    |changeClass sourceWithNamespace|

    superClassName isNil ifTrue:[
        self setupFromSource
        superClassName isNil ifTrue:[
            self error:'Should not happen'
        ].
    ].
    (Smalltalk classNamed:superClassName) isNil ifTrue:[
        Class undeclared:superClassName
    ].

    "/ here, using the current namespace as provided by the query or the override,
    "/ generate a class definition with full names (i.e. incl. ns prefix).
    sourceWithNamespace := self source. 
    "/ as the namespace is already in the definition string, make sure that the namespace is not
    "/ added twice, by anwering Smalltalk now!!
    Class nameSpaceQuerySignal 
        answer:Smalltalk
        do:[
            Parser evaluate:sourceWithNamespace.       
        ].

    package notNil ifTrue:[
        changeClass := self changeClass.
        changeClass notNil ifTrue:[
            changeClass setPackage:package.
        ].
    ].

    "
      (ClassDefinitionChange className: #TestB source: 'TestA subclass: #TestB
          instanceVariableNames:''''
          classVariableNames:''''
          poolDictionaries:''''
          category:''* remove me *''')
          apply
    "

    "Modified: / 08-11-2010 / 16:10:16 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 06-10-2011 / 17:01:58 / cg"
! !

!ClassDefinitionChange methodsFor:'comparing'!

isConflict
    "true if this change is different than what is already in the image
     (i.e. it overwrites some existing code)"

    |cls|

    (cls := self changeClass) isNil ifTrue:[^ false].
    cls superclass name ~= superClassName ifTrue:[ ^ true ].
    cls instanceVariableString ~= instanceVariableNames ifTrue:[ ^ true ].
    cls classVariableString ~= classVariableNames ifTrue:[ ^ true ].
    cls class instanceVariableString ~= classInstanceVariableNames ifTrue:[ ^ true ].
    cls sharedPoolNames ~= poolDictionaries ifTrue:[ ^ true ].
    cls category ~= category ifTrue:[ ^ true ].
    cls isPrivate ifTrue:[
        cls owningClass name ~= owningClassName ifTrue:[ ^ true ].
    ].
    cls definitionSelector ~= self definitionSelector ifTrue:[ ^ true ].
    ^  false
!

isForSameAs:changeB
    "return true, if the given change represents a change for the same
     thingy as the receiver (i.e. same method, same definition etc.)."

    changeB isClassDefinitionChange ifFalse:[^ false].   
    ^ self className = changeB className.
!

sameAs:changeB
    "return true, if the given change represents the same change as the receiver."

    (self isForSameAs:changeB) ifFalse:[^ false].
    ^ self sameSourceAs:changeB



! !

!ClassDefinitionChange methodsFor:'converting'!

asAntiChange

    ^ClassRemoveChange className: self className

    "Created: / 02-11-2009 / 11:11:29 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!ClassDefinitionChange methodsFor:'printing & storing'!

definitionString
    ^ self definitionStringInNamespace: (self nameSpaceOverride)
!

definitionStringInNamespace: nsOrNil
    |classNameUsed superClassNameUsed selPart ownerNameUsed|

    objectType == #variable ifTrue:[
        "/ a visualWorks static variable definition change.
        "/ kludge to be here - should be a separate StaticVariableDefinitionChange
        ^ String streamContents:[:stream |
            nsOrNil notNil ifTrue:[
                stream nextPutAll:((nsOrNil asCollectionOfSubstringsSeparatedBy:$.) asStringWith:'::')
            ] ifFalse:[
                self halt:'can this happen ?'.
                stream nextPutAll:'Smalltalk'
            ].

            stream 
                nextPutAll:' addClassVarName:';
                nextPutAll:className asString storeString
          ].
    ].

    superClassNameUsed := self superClassName.
    "/ careful with private classes: the definition MUST give the
    "/ local name as argument, not the full name
    self isPrivateClassDefinitionChange ifTrue:[
        classNameUsed := self localClassName.
    ] ifFalse:[
        classNameUsed := className.
        nsOrNil notNil ifTrue:[
            classNameUsed := nsOrNil,'::',classNameUsed.
        ].
    ].

    "/ selPart is the subclass:/variableSubclass/variableByteSubclass:/... - part
    selPart := (self definitionSelector ? #'subclass:instanceVariableNames:classVariableNames:poolDictionaries:category:')
                    keywords first.         

    ^ String streamContents:[:stream |
        stream 
            nextPutAll:superClassNameUsed;
            nextPutAll:' ',selPart;
            nextPutLine: classNameUsed asSymbol storeString;
            tab;
            nextPutAll:'instanceVariableNames:';
            nextPutLine:(instanceVariableNames ? '') storeString;
            tab;
            nextPutAll:'classVariableNames:';
            nextPutLine:(classVariableNames ? '') storeString;
            tab;
            nextPutAll:'poolDictionaries:';
            nextPutLine:(poolDictionaries ? '') storeString.
        self isPrivateClassDefinitionChange ifTrue:[
            ownerNameUsed := self owningClassName.    
            nsOrNil notNil ifTrue:[
                ownerNameUsed := nsOrNil,'::',ownerNameUsed.
            ].
            stream 
                tab;
                nextPutAll:'privateIn:';
                nextPutAll:ownerNameUsed
        ] ifFalse:[
            stream 
                tab;
                nextPutAll:'category:';
                nextPutAll:(category ? '') asString storeString
        ].
      ]

    "Modified: / 13-06-2012 / 13:01:58 / cg"
    "Modified: / 30-04-2013 / 19:52:35 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

definitionStringWithoutNamespace
    "cg - huh - who needs that? (the definitionString already does NOT include the classes namespace)"

    |ns classNameUsed superClassNameUsed|

    ns := self nameSpaceOverride.

    objectType == #variable ifTrue:[
        ^ String streamContents:[:stream |
            ns notNil ifTrue:[
                stream 
                    nextPutAll:((ns asCollectionOfSubstringsSeparatedBy:$.) asStringWith:'::')
            ] ifFalse:[
                self halt:'can this happen ?'.
                stream 
                    nextPutAll:'Smalltalk'
            ].

            stream 
                nextPutAll:' addClassVarName:';
                nextPutAll:className asString storeString
          ].
    ].

    superClassNameUsed := self superClassName.
    classNameUsed := self classNameWithoutNamespace.

    ^ String streamContents:[:stream |
        self isPrivateClassDefinitionChange ifFalse:[
            stream 
                nextPutAll:superClassNameUsed;
                nextPutAll:' subclass:';
                nextPutAll: classNameUsed asSymbol storeString
                ;
                cr;
                tab;
                nextPutAll:'instanceVariableNames:';
                nextPutAll:(instanceVariableNames ? '') storeString;
                cr;
                tab;
                nextPutAll:'classVariableNames:';
                nextPutAll:(classVariableNames ? '') storeString;
                cr;
                tab;
                nextPutAll:'poolDictionaries:';
                nextPutAll:(poolDictionaries ? '') storeString;
                cr;
                tab;
                nextPutAll:'category:';
                nextPutAll:(category ? '') storeString;
                cr
        ] ifTrue:[
            stream 
                nextPutAll:superClassNameUsed;
                nextPutAll:' subclass:';
                nextPutAll: (self className copyFrom: owningClassName size + 3) asSymbol storeString
                ;
                cr;
                tab;
                nextPutAll:'instanceVariableNames:';
                nextPutAll:(instanceVariableNames ? '') storeString;
                cr;
                tab;
                nextPutAll:'classVariableNames:';
                nextPutAll:(classVariableNames ? '') storeString;
                cr;
                tab;
                nextPutAll:'poolDictionaries:';
                nextPutAll:(poolDictionaries ? '') storeString;
                cr;
                tab;
                nextPutAll:'privateIn:';
                nextPutAll:
                    ((ns := self nameSpaceName) isNil
                        ifTrue:[owningClassName]
                        ifFalse:[owningClassName copyFrom: ns size + 3]);
                cr
        ]
    ]

    "Modified: / 06-10-2011 / 17:02:05 / cg"
    "Created: / 20-03-2012 / 16:37:56 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

printOn:aStream
    aStream 
        nextPutAll:className; nextPutAll:' {definition}'

    "Modified: / 12-10-2006 / 17:48:28 / cg"
!

printWithoutClassNameOn:aStream
    aStream nextPutAll:('definition of ' , className)


! !

!ClassDefinitionChange methodsFor:'queries'!

definitionSelector
    definitionSelector isNil ifTrue:[
        self setupFromSource.
    ].
    ^ definitionSelector

    "Modified: / 11-10-2006 / 14:11:44 / cg"
!

definitionSelector:aSelector
    definitionSelector := aSelector

    "Created: / 13-06-2012 / 12:45:02 / cg"
!

fullOwningClassName
    "the owner's name, including its namespace"

    |nm ns|

    nm := self owningClassName.
    nm isNil ifTrue:[^ nil].

    (ns := self nameSpaceOverride) notNil ifTrue:[
        ^ ns,'::',nm
    ].
    ^ nm
!

isClassDefinitionChange
    ^ true
!

isPrivateClassDefinitionChange
    "compute lazily; remember in private"

    private isNil ifTrue:[
        (className includes:$:) ifFalse:[
            "/ cannot be private
            private := false
        ] ifTrue:[
            source isNil ifTrue:[^ false ].
            (source includesString:'private') ifFalse:[
                private := false.
            ] ifTrue:[
"/                (self changeClass notNil
"/                and:[self changeClass isLoaded not]) ifTrue:[
"/                    "/ cannot be private
"/                    private := false
"/                ] ifTrue:[
                    self setupFromSource.
"/                ].
            ].
        ].
    ].
    ^ private

    "Created: / 11-10-2006 / 14:19:03 / cg"
    "Modified: / 16-11-2006 / 16:34:19 / cg"
!

owningClassName
    "the owner's name, excluding the namespace"

    self isPrivateClassDefinitionChange ifTrue:[
        owningClassName isNil ifTrue:[
            self setupFromSource.
        ].
    ].
    ^ owningClassName

    "Created: / 12-10-2006 / 23:07:25 / cg"
!

owningClassName:aStringOrSymbol
    owningClassName := aStringOrSymbol

    "Created: / 30-08-2010 / 13:55:37 / cg"
! !

!ClassDefinitionChange methodsFor:'special'!

installAsAutoloadedClassIfPublicWithFilename:aFilenameString
    "install the class defined by this change as autoloaded.
     Skip private classes.
     Enter class file name as abbreviation"

    |parseTree sel cat clsName cls catIdx pkg|

    private == true ifTrue:[^ self].

    parseTree := Parser parseExpression:self source.
    parseTree isMessage ifFalse:[
        self error:'bad change source'.
    ].

    sel := parseTree selector.
    (sel endsWith:':privateIn:') ifTrue:[^ self].

    catIdx := sel asSymbol keywords indexOf:'category:'.  
    catIdx ~~ 0 ifTrue:[
        cat := (parseTree args at:catIdx) evaluate.
    ].

    clsName := self fullClassName "className" asSymbol.
    cls := Smalltalk at:clsName.

    pkg := package ? Project current package.   

    (cls isNil 
     or:[cls isBehavior not
     or:[cls isLoaded not]]) ifTrue:[
        |autoloadedClass|

        autoloadedClass := Smalltalk 
           installAutoloadedClassNamed:clsName 
           category:cat 
           package:pkg 
           revision:nil.
        aFilenameString notNil ifTrue:[
            autoloadedClass setClassFilename:aFilenameString.
            "/ Smalltalk setFilename:aFilenameString forClass:clsName package:pkg.
        ]
    ] ifFalse:[
        cls notNil ifTrue:[
            cls isBehavior ifTrue:[
                cls package ~= pkg ifTrue:[
Transcript showCR:('Autoloaded class: %1 not installed (package would change from %2 to %3)' 
                        bindWith:clsName with:cls package with:pkg).
                ]
            ].
        ].
    ].

    "Modified: / 03-07-2011 / 18:34:05 / cg"
!

invalidateSource
    "internal - flush the sourceString if it got invalidated due to a
     className, superclassName, etc... change"

    source := nil.
!

setupFromSource
    "Extract data from class definition string in source. 
     WARNING: This overwrites values in instvars!!"

    |parseTree catIdx poolIdx instVarIdx classVarIdx |

    source notNil ifTrue:[
        parseTree := Parser parseExpression:source.
        (parseTree notNil and:[parseTree isMessage]) ifFalse:[
            self error:'bad change source' mayProceed:true.
            ^ self
        ].

        definitionSelector := parseTree selector.

        private := (definitionSelector endsWith:':privateIn:').
        private ifTrue:[
            owningClassName := parseTree args last name.
        ].

        catIdx := definitionSelector keywords indexOf:'category:'.  
        catIdx ~~ 0 ifTrue:[
            category := (parseTree args at:catIdx) evaluate.
        ].

        poolIdx := definitionSelector keywords indexOf:'poolDictionaries:'.  
        poolIdx ~~ 0 ifTrue:[
            poolDictionaries := (parseTree args at:poolIdx) evaluate.
        ].

        instVarIdx := definitionSelector keywords indexOf:'instanceVariableNames:'.
        instVarIdx ~~ 0 ifTrue:[
            instanceVariableNames := (parseTree args at:instVarIdx) value.
        ].

        classVarIdx := definitionSelector keywords indexOf:'classVariableNames:'.
        classVarIdx ~~ 0 ifTrue:[
            classVariableNames := (parseTree args at:classVarIdx) value.
        ].



        superClassName := parseTree receiver name.
        self assert:(superClassName notNil).
    ].

    "Created: / 11-10-2006 / 14:10:02 / cg"
    "Modified: / 13-06-2012 / 12:25:10 / cg"
    "Modified: / 30-01-2013 / 12:01:43 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!ClassDefinitionChange methodsFor:'visiting'!

acceptChangeVisitor:aVisitor
    ^ aVisitor visitClassDefinitionChange:self.

    "Created: / 25-11-2011 / 17:13:13 / cg"
! !

!ClassDefinitionChange class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libbasic3/ClassDefinitionChange.st,v 1.82 2013-05-01 09:31:30 +0100 vrany Exp $'
!

version_CVS
    ^ '$Header: /cvs/stx/stx/libbasic3/ClassDefinitionChange.st,v 1.82 2013-05-01 09:31:30 +0100 vrany Exp $'
!

version_HG

    ^ '$Changeset: <not expanded> $'
!

version_SVN
    ^ '§Id:: ClassDefinitionChange.st 1936 2012-07-24 15:47:21Z vranyj1                                                             §'
! !