VSEPackageFileSourceWriter.st
author Claus Gittinger <cg@exept.de>
Thu, 05 Mar 2020 11:17:28 +0100
changeset 4561 eace75531554
parent 3885 82006a572d15
permissions -rw-r--r--
#UI_ENHANCEMENT by cg class: SourceCodeManagerUtilities changed: #compareClassWithRepository:askForRevision: typos: genitive of class is class's - not classes.

"
 COPYRIGHT (c) 2015 by eXept Software AG
              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' }"

"{ NameSpace: Smalltalk }"

VSEFileSourceWriter subclass:#VSEPackageFileSourceWriter
	instanceVariableNames:'projectDefinitionClass classesToBeInitialized'
	classVariableNames:''
	poolDictionaries:''
	category:'Kernel-Classes-Support'
!

!VSEPackageFileSourceWriter class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 2015 by eXept Software AG
              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
"
    fileout in a format which can be read by visualAge.
    For transporting software.
"
!

examples
"
                                                        [exBegin]
    |s|

    s := 'test.st' asFilename writeStream.
    [
        VSEPackageFileSourceWriter new
            fileOut:OrderedCollection on:s
    ] ensure:[
        s close
    ]
                                                        [exEnd]

                                                        [exBegin]
    |s|

    s := '' writeStream.
    [
        VSEPackageFileSourceWriter new
            fileOut:OrderedCollection on:s
    ] ensure:[
        s close
    ].
    s contents
                                                        [exEnd]
"
! !

!VSEPackageFileSourceWriter methodsFor:'source writing'!

fileOut:aClass on:aStream withTimeStamp:stampIt withInitialize:initIt withDefinition:withDefinition methodFilter:methodFilter encoder:encoderOrNil
    |commentOrDocumentation skipDocumentationMethod hasInitialize|

    hasInitialize := false.
    skipDocumentationMethod := false.

    aStream nextPutAll:'Class '.
    aStream nextPutAll:(self rewrittenClassNameOf:aClass).
    aStream space.
    aClass superclass isNil ifTrue:[
        self error:'unimplemented: nil superclass'.
    ].
    aStream nextPutAll:(self rewrittenClassNameOf:aClass superclass).
    aStream nextPutLine:'!!'.

    (commentOrDocumentation := aClass comment) isEmptyOrNil ifTrue:[
        (commentOrDocumentation := aClass commentOrDocumentationString) notEmptyOrNil ifTrue:[
            skipDocumentationMethod := true
        ].
    ].
    commentOrDocumentation notEmptyOrNil ifTrue:[
        self putEntry:'Property' value:'Comment' on:aStream.
        self putEntry:'' value:commentOrDocumentation on:aStream.
    ].

    aClass isVariable ifTrue:[
        aClass isPointers ifTrue:[
            aStream nextPutLine:'IndexableObject!!'.
        ] ifFalse:[
            self error:'unimplemented: non-pointer indexable'.
        ]
    ] ifFalse:[
        aStream nextPutLine:'NonIndexable!!'.
    ].
    aClass instanceVariableNames do:[:v |
        aStream nextPutLine:'InstanceVariable ',v,'!!'.
    ].
    aClass classVariableNames do:[:v |
        aStream nextPutLine:'ClassVariable ',v,'!!'.
    ].
    aClass class instanceVariableNames do:[:v |
        aStream nextPutLine:'ClassInstanceVariable ',v,'!!'.
    ].
    aClass realSharedPoolNames do:[:nm |      
        aStream nextPutLine:'Pool ',nm,'!!'.
    ].

    aClass methodDictionary keys asNewOrderedCollection sort do:[:sel |
        self fileOutMethod:(aClass methodDictionary at:sel) isExtension:false on:aStream.
    ].
    aClass class methodDictionary keys asNewOrderedCollection sort do:[:sel |
        (skipDocumentationMethod and:[sel == #documentation]) ifTrue:[
            "/ skip method
        ] ifFalse:[
            (skipVersionMethods and:[sel == #version or:[sel startsWith:'version_']]) ifTrue:[
                "/ skip method
            ] ifFalse:[
                sel == #initialize ifTrue:[
                    hasInitialize := true.
                ].
                self fileOutMethod:(aClass class methodDictionary at:sel) isExtension:false on:aStream.
            ].
        ].
    ].

    hasInitialize ifTrue:[
        |initializerCode|

        initializerCode := (self rewrittenClassNameOf:aClass), ' initialize.'.
        self putEntry:'Initialization' value:initializerCode on:aStream.
    ].

    self putEntry:'EndClass' value:nil on:aStream.
!

fileOutMethod:aMethod isExtension:isExtension on:aStream
    |sel mclass isMeta rewriteQuery originalSource possiblyRewrittenSource
     date time user whatVersion methodVersionString info sourceCodeManager|

    sel := aMethod selector.
    mclass := aMethod mclass.
    isMeta := mclass isMetaclass.

    isExtension ifTrue:[
        "/ skip - leads to trouble
        ((sel == #date) and:[mclass == Timestamp]) ifTrue:[
            Transcript showCR:'- skip TimeStamp >> date'.
            ^ self.
        ].
    ].

    projectDefinitionClass isNil ifTrue:[
        "/ 02/03/15 04:26:18 PM by unknown
        date := Date today.
        time := Time now.
        user := OperatingSystem getFullUserName.
        whatVersion := 'time exported'.
    ] ifFalse:[
        sourceCodeManager := projectDefinitionClass sourceCodeManager.
        isExtension ifTrue:[
            info := projectDefinitionClass extensionsRevisionInfoForManager:sourceCodeManager.
        ] ifFalse:[
            info := mclass theNonMetaclass revisionInfoOfManager:sourceCodeManager.
        ].
        date := Date readFrom:(info date).
        time := Time readFrom:(info time).
        user := info user.
        whatVersion := 'cvs version ',(info revision).
    ].
    methodVersionString := '%1 %2 by %3 (%4)'
                                bindWith:(date printStringFormat:'%m/%d/%Y')
                                with:(time printStringFormat:'%u:%m:%s %A')
                                with:user  
                                with:whatVersion.   

    originalSource := aMethod source.
    rewriteQuery := AbstractSourceFileWriter methodSourceRewriteQuery new.
    rewriteQuery method:aMethod source:originalSource.
    possiblyRewrittenSource := (rewriteQuery query) ? originalSource.

    isExtension ifTrue:[
        "/ hack for LPReal -> move to Float, unles defined there.
        mclass theNonMetaclass == LimitedPrecisionReal ifTrue:[
            (Float implements:sel) ifTrue:[
                self halt:'method skipped'.
                ^ self.
            ].
            "/ self halt:'save for Float instead of LPReal'.
            mclass := mclass isMeta ifTrue:[Float class] ifFalse:[Float].
            Transcript showCR:'- moved extension method from LimitedPrecisionReal to Float'.
        ].
        aStream nextPutAll:'MethodDefinition ',(self rewrittenClassNameOf:mclass theNonMetaclass).
    ] ifFalse:[
        aStream nextPutAll:'Method'.   
    ].
    isMeta ifTrue:[
        aStream nextPutLine:' class ',sel,'!!'.
    ] ifFalse:[
        aStream nextPutLine:' instance ',sel,'!!'.
    ].

    aStream space.
    self putString:possiblyRewrittenSource on:aStream.
    aStream nextPutLine:'!!'.
    self putEntry:'Property' value:'.Version' on:aStream.
    self putEntry:'' value:methodVersionString on:aStream.
    self putEntry:'Property' value:'Categories' on:aStream.
    self putEntry:'' value:aMethod category on:aStream.
!

fileOutPackage:packageID on:aStream
    |classesToFileout methodsToFileOut rewriter|

    projectDefinitionClass := packageID asPackageId projectDefinitionClass.
    classesToBeInitialized := OrderedCollection new.

    aStream lineEndCRLF.

    "/ make sure that everything is loaded.
    projectDefinitionClass notNil ifTrue:[
        projectDefinitionClass autoload.
        projectDefinitionClass ensureFullyLoaded.
        classesToFileout := Smalltalk allClassesInPackage:packageID.
    ] ifFalse:[
        classesToFileout := Smalltalk allClassesInPackage:packageID.
        classesToFileout := classesToFileout collect:[:each | each autoload].
    ].

    classesToFileout := classesToFileout reject:[:cls | cls isSubclassOf: ProjectDefinition ].
    classesToFileout topologicalSort:[:a :b | b isSubclassOf:a].

    classesToFileout do:[:cls | 
        cls isPrivate ifTrue:[
            self error:'Cannot file out private class: ',cls name.
        ].
    ].

    rewriter := self class vseSourceRewriter new.
    rewriter classNamesToRewrite:classNameTranslations.

    methodsToFileOut := self extensionMethodsFor:packageID.

    self activityNotification:'checking for unportable unicode...'.
    classesToFileout do:[:eachClass |
        self ensureNoUnicodeInClass:eachClass
    ].
    methodsToFileOut do:[:eachClass |
        self ensureNoUnicodeInMethod:eachClass
    ].

    self writeHeaderOn:aStream.

    AbstractSourceFileWriter methodSourceRewriteQuery handle:[:rewriteQuery |
        |method source|

        method := rewriteQuery method.
        source := rewriteQuery source.
        source := rewriter rewriteMethod:method.
        rewriteQuery proceedWith:source.
    ] do:[
        classesToFileout do:[:eachClass |
            self activityNotification:'exporting ',eachClass name,'...'.
            self fileOut:eachClass on:aStream.
        ].

        "/ fileout extensions
        self activityNotification:'exporting extensions...'.
        methodsToFileOut do:[:eachMethod |
            self fileOutMethod:eachMethod isExtension:true on:aStream.
            aStream cr.
        ].
    ].

    self activityNotification:'done.'.
!

putEntry:key value:value on:aStream
    key notNil ifTrue:[
        aStream nextPutAll:key.
    ].
    value notNil ifTrue:[
        aStream space.    
        self putString:value on:aStream
    ].
    aStream nextPutLine:'!!'.
!

putString:aString on:aStream
    "as the string is written (and should be counted) with cr-lf,
     count the returns and the size compute number"

    |numCRs|

    numCRs := aString occurrencesOf:Character cr.

    aStream nextPutAll:'<S'.
    aStream nextPutAll:(aString size + numCRs) asString.
    aStream nextPutAll:'>'.
    aStream nextPutAll:aString.
!

writeHeaderOn:aStream
    |releaseNr revisionNr author vsePackageName packageComment|

    releaseNr := 1.
    revisionNr := 1.
    author := OperatingSystem getFullUserName.

    projectDefinitionClass notNil ifTrue:[
        vsePackageName := projectDefinitionClass perform:#vsePackageName ifNotUnderstood:nil.
    ].
    vsePackageName isNil ifTrue:[
        |action optionLabels options|

        projectDefinitionClass notNil ifTrue:[
            optionLabels := #('Cancel' 'No, Browse' 'Yes').
            options := #(#abort #browse #yes)
        ] ifFalse:[
            optionLabels := #('Cancel' 'Yes').
            options := #(#abort #yes)
        ].
        action := OptionBox
                          request:(Dialog resources stringWithCRs:'No vsePackageName defined in ProjectDefinition.\\Proceed (using ST/X package name)?')
                          label:'VSE Package Export'
                          image:(WarningBox iconBitmap)
                          buttonLabels:(Dialog resources array:optionLabels)
                          values:options
                          default:(projectDefinitionClass isNil ifTrue:#abort ifFalse:#browse)
                          onCancel:#abort.
        action == #abort ifTrue:[
            AbortOperationRequest raise
        ].
        action == #browse ifTrue:[
            UserPreferences browserClass openInClass:projectDefinitionClass theMetaclass.
            AbortOperationRequest raise
        ].
    ].

    projectDefinitionClass notNil ifTrue:[
        releaseNr := projectDefinitionClass perform:#vseReleaseNr ifNotUnderstood:nil.
        releaseNr isNil ifTrue:[
            releaseNr := projectDefinitionClass revisionInfo majorVersion 
        ].
        revisionNr := projectDefinitionClass perform:#vseRevisionNr ifNotUnderstood:nil. 
        revisionNr isNil ifTrue:[
            revisionNr := projectDefinitionClass revisionInfo minorVersion 
        ].
        author := projectDefinitionClass perform:#vseAuthor ifNotUnderstood:nil.
        author isNil ifTrue:[
            author := projectDefinitionClass revisionInfo author 
        ].
        packageComment := projectDefinitionClass commentOrDocumentationString.
    ].

    self putEntry:'Header' value:nil on:aStream.
    self putEntry:'Type ' value:'Package' on:aStream.
    self putEntry:'Format ' value:'ExtendedFileOut' on:aStream.
    self putEntry:'Version ' value:'1.0' on:aStream.
    self putEntry:'EndHeader' value:nil on:aStream.

    self putEntry:'Package ' value:(vsePackageName ? (projectDefinitionClass package) ? 'unnamed') on:aStream.
    self putEntry:'Property' value:'Release' on:aStream.
    self putEntry:nil value:(releaseNr asString) on:aStream.
    self putEntry:'Property' value:'RevisionNumber' on:aStream.
    self putEntry:nil value:(revisionNr asString) on:aStream.
    self putEntry:'Property' value:'Author' on:aStream.
    self putEntry:nil value:author on:aStream.
    packageComment notEmptyOrNil ifTrue:[
        self putEntry:'Property' value:'Comment' on:aStream.
        self putEntry:nil value:packageComment on:aStream.
    ].
! !

!VSEPackageFileSourceWriter class methodsFor:'documentation'!

version
    ^ '$Header$'
!

version_CVS
    ^ '$Header$'
! !