mercurial/HGSourceCodeManager.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Tue, 04 Dec 2012 10:14:28 +0000
changeset 126 952efea00dd2
parent 125 4d1414d5f1ba
child 128 11b1e4b11fd3
permissions -rw-r--r--
More SourceCodeManager API. Added #streamForClass:fileName:revision:directory:module:cache:

"{ Package: 'stx:libscm/mercurial' }"

SCMAbstractSourceCodeManager subclass:#HGSourceCodeManager
	instanceVariableNames:''
	classVariableNames:''
	poolDictionaries:''
	category:'SCM-Mercurial-StX'
!


!HGSourceCodeManager class methodsFor:'accessing'!

repositoryNameForPackage:packageId
    "Return the repository ULR for the given package. 
     Used for testing/debugging source code management configuration"

    ^ HGRepository discover: (Smalltalk getPackageDirectoryForPackage: packageId)

    "Modified: / 13-11-2012 / 22:16:54 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGSourceCodeManager class methodsFor:'accessing-classes'!

commitDialogClass
    "Answer a dialog class to be used for commits"

    ^HGCommitDialog

    "Created: / 13-11-2012 / 23:59:08 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

packageRegistryClass
    "Answer the package manager class used to get
     package models"
    
    ^ HGPackageModelRegistry

    "Created: / 13-11-2012 / 23:59:05 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGSourceCodeManager class methodsFor:'basic access'!

checkinClass:aClass fileName:classFileName directory:packageDir module:moduleDir source:sourceFile logMessage:logMessage force:force
    "checkin of a class into the source repository.
     Return true if ok, false if not."

    ^ self shouldImplement
!

checkoutModule:aModule directory:aPackage andDo:aBlock
    "check out everything from a package into a temporary directory.
     Then evaluate aBlock, passing the name of that temp-directory.
     Afterwards, the tempDir is removed.
     Return true, if OK, false if any error occurred."

    ^ self shouldImplement
!

streamForClass:aClass fileName:classFileName revision:revision directory:packageDir module:moduleDir cache:doCache
    "extract a classes source code and return an open readStream on it.
     A revision of nil selects the current (in image) revision.
     The classes source code is extracted using the revision and the sourceCodeInfo,
     which itself is extracted from the classes packageString."

    | pkg repo file |

    pkg := HGPackageModel named: (moduleDir , ':' , packageDir).
    repo := pkg repository.
    file := (repo @ revision) / pkg repositoryRoot / classFileName.

    doCache ifTrue:[
        ^SourceCodeCache default
            streamForClass:aClass 
            fileName:classFileName 
            revision:revision 
            repository: 'svn' "TODO: Use repository ID here" 
            module:moduleDir 
            directory:packageDir 
            ifAbsent: [:destination|
                ActivityNotification notify: ('Checking out ', classFileName , '@' , revision  , '...').
                [
                    file copyTo: destination.    
                    destination exists ifTrue:[
                        destination readStream
                    ] ifFalse:[
                        nil
                    ]
                ] on: SVN::SVNError do:[
                    nil                    
                ]
            ]            
    ] ifFalse:[
        ^file readStream.
    ]

    "Modified: / 04-12-2012 / 02:04:00 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGSourceCodeManager class methodsFor:'basic administration'!

checkForExistingContainer:fileName inModule:moduleName directory:dirName
    "check for a container to be present"

    ^ self shouldImplement
!

checkForExistingModule:moduleName
    "check for a module directory to be present"

    ^ self shouldImplement
!

checkForExistingModule:moduleDir directory:packageDir
    "check for a package directory to be present"

    ^ self shouldImplement
!

createContainerFor:aClass inModule:moduleName directory:dirName container:fileName
    "create a new container & check into it an initial version of aClass"

    ^ self shouldImplement
!

createModule:moduleName
    "create a new module directory"

    ^ self shouldImplement
!

createModule:module directory:directory
    "create a new package directory"

    ^ self shouldImplement
!

initialRevisionStringFor:aClass inModule:moduleDir directory:packageDir container:fileName
    "return a string usable as initial revision string"

    ^ self shouldImplement
!

revisionLogOf:clsOrNil fromRevision:rev1OrNil toRevision:rev2OrNil numberOfRevisions:limitOrNil fileName:classFileName directory:packageDir module:moduleDir
    "Actually do return a revisionLog. The main worker method. This must be implemented by a 
     concrete source-code manager. The interface of this method is just crazy!!

     If numRevisionsOrNil is notNil, it limits the number of revision records returned -
     only numRevions of the newest revision infos will be collected.

     The returned information is a structure (IdentityDictionary)
     filled with:
            #container          -> the RCS/CVS container file name 
            #cvsRoot            -> the CVS root (repository) 
            #filename           -> the actual source file name
            #newestRevision     -> the revisionString of the newest revision
            #numberOfRevisions  -> the number of revisions in the container (nil for all)
            #revisions          -> collection of per-revision info (see below)

            firstRevOrNil / lastRevOrNil specify from which revisions a logEntry is wanted:
             -If firstRevOrNil is nil, the first revision is the initial revision
              otherwise, the log starts with that revision.
             -If lastRevOrNil is nil, the last revision is the newest revision
              otherwise, the log ends with that revision.

             -If both are nil, all logEntries are extracted.
             -If both are 0 (not nil), no logEntries are extracted (i.e. only the header).

            per revision info consists of one record per revision:

              #revision              -> the revision string
              #author                -> who checked that revision into the repository
              #date                  -> when was it checked in
              #state                 -> the RCS state
              #numberOfChangedLines  -> the number of changed line w.r.t the previous
              #logMessage            -> the checkIn log message

            revisions are ordered newest first 
            (i.e. the last entry is for the initial revision; the first for the most recent one)
            Attention: if state = 'dead' that revision is no longer valid.
    "

    | pkg repo path info newest rev limit stop log |

    info := IdentityDictionary new.
    pkg := HGPackageModel named: (moduleDir , ':' , packageDir).
    repo := pkg repository.
    path := pkg repositoryRoot.

    newest := repo workingCopy heads first.
    [   
        newest notNil and:
            [([ newest / path / classFileName ] on: HGError do:[:ex | nil ]) isNil]
    ] whileTrue:[
        newest := newest parent1.
    ].


    info at:#container          put: classFileName.         "/ -> the revision string
    info at:#cvsRoot            put: repo pathName.         "/ -> the CVS root (repository)
    info at:#filename           put: classFileName.         "/ -> the actual source file name
    info at:#newestRevision     put: newest id printString. "/-> the revisionString of the newest revision

    (rev1OrNil == 0 and:[rev2OrNil == 0]) ifTrue:[
        limit := 1
    ] ifFalse:[
        limit := limitOrNil 
    ].
    log := OrderedCollection new.
    rev := rev1OrNil isNil ifTrue:[newest] ifFalse:[repo @ rev1OrNil].

    stop := false.
    [ stop ] whileFalse:[
        | entry |

        entry :=  IdentityDictionary new.
        entry at:#revision              put: rev id printString."/ -> the revision string
        entry at:#author                put: rev author."/ -> who checked that revision into the repository
        entry at:#date                  put: rev timestamp printString."/ -> when was it checked in
        entry at:#state                 put: 'Exp'. "/ -> the RCS state
        entry at:#numberOfChangedLines  put: 'N/A'. "/ -> the number of changed line w.r.t the previous
        entry at:#logMessage            put: rev message."/ -> the checkIn log message.
        log add: entry.

        limit notNil ifTrue:[limit := limit - 1].
        stop :=
            rev isNil 
                or:[limit == 0
                or:[(rev2OrNil notNil and:[rev2OrNil = rev id printString])
                or:[([ rev / path / classFileName . false ] on: HGError do:[:ex | true ])]]].
        rev := rev parent1.
        
    ].
    info at: #revisions put: log.

    ^info

    "Modified: / 04-12-2012 / 01:34:09 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGSourceCodeManager class methodsFor:'queries'!

isResponsibleForPackage:aStringOrSymbol
    "Returns true if the manager can handle source code for given package.
     
     Answering true does not imply that receiver is configured default
     manager for that package, it only means that it has a repository
     configured for given package."

    "No configuration yet, so let's scan the working copy"

    | pkgDir |

    pkgDir := Smalltalk getPackageDirectoryForPackage: aStringOrSymbol.
    ^pkgDir notNil and:[(HGRepository discover: pkgDir) notNil]

    "Modified: / 14-11-2012 / 00:24:31 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

managerTypeName
    "superclass AbstractSourceCodeManager class says that I am responsible to implement this method"

    ^ 'Mercurial+'

    "Modified: / 13-11-2012 / 22:40:31 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

managerTypeNameShort
    ^ 'HG'

    "Created: / 06-10-2012 / 17:10:54 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 13-11-2012 / 22:40:25 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

nameOfVersionMethodForExtensions
    "that is the old name; now, we use extensionsVersion_<SCM>"

    ^ #'extensionsVersion_HG'

    "Modified (comment): / 29-09-2011 / 13:27:04 / cg"
    "Modified: / 13-11-2012 / 22:40:38 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

nameOfVersionMethodInClasses
    "that is the old name; now, we use version_<SCM>"

    ^ #'version_HG'

    "Modified (comment): / 29-09-2011 / 13:27:09 / cg"
    "Modified: / 13-11-2012 / 22:40:43 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

performsCompilabilityChecks
    ^true

    "Created: / 01-12-2012 / 01:01:33 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

settingsApplicationClass
    "link to my settings application (needed for the settings dialog"

    ^ HGSourceCodeManagementSettingsAppl

    "Modified: / 13-11-2012 / 22:40:49 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

versionMethodTemplateForJavaScriptFor:aSelector
    "do not make the thing below a single string - otherwise
     it would get expanded by the sourcecodemanager, which we do not want here"

    ^
"'function ',"aSelector,'() {

    return "$Changeset: <not expanded>"$;
}'

    "Created: / 07-10-2012 / 00:23:26 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 30-11-2012 / 21:32:12 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

versionMethodTemplateForRubyFor:aSelector
    "do not make the thing below a single string - otherwise
     it would get expanded by the sourcecodemanager, which we do not want here"

    ^
'def self.',aSelector,'()

    return "$Changeset: <not expanded>$"
end'

    "Created: / 07-10-2012 / 00:22:55 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 30-11-2012 / 21:32:16 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

versionMethodTemplateForSmalltalkFor:aSelector
    "do not make the thing below a single string - otherwise
     it would get expanded by the sourcecodemanager, which we do not want here"

    ^
aSelector,'

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

    "Created: / 07-10-2012 / 00:21:42 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 30-11-2012 / 21:32:22 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGSourceCodeManager class methodsFor:'source code administration'!

getExistingContainersInModule:aModule directory:aPackage
    "{ Pragma: +optSpace }"

    ^ self shouldImplement
!

getExistingDirectoriesInModule:aModule
    "{ Pragma: +optSpace }"

    ^ self shouldImplement
!

getExistingModules
    "{ Pragma: +optSpace }"

    ^ self shouldImplement
!

removeContainer:container inModule:module directory:directory
    "remove a container"

    ^ self shouldImplement
!

revisionInfoFromString:aString
    "{ Pragma: +optSpace }"

    ^ HGRevisionInfo fromString: aString

    "Modified: / 20-11-2012 / 23:26:35 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

revisionInfoFromString: vsnString inClass: class
    | revInfo |

    revInfo := self revisionInfoFromString: (class binaryRevisionString ? vsnString).
    revInfo className: class name.
    ^revInfo

    "Created: / 30-11-2012 / 21:48:42 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!HGSourceCodeManager class methodsFor:'subclass responsibility'!

reportHistoryLogSince:timeGoal filterSTSources:filter filterUser:userFilter filterRepository:repositoryFilter filterModules:moduleFilter inTo:aBlock
    "superclass AbstractSourceCodeManager class says that I am responsible to implement this method"

    ^ self shouldImplement
! !

!HGSourceCodeManager class methodsFor:'documentation'!

version_HG

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

version_SVN
    ^ '§Id::                                                                                                                        §'
! !