DataBaseSourceCodeManager.st
author vrany
Sun, 01 Jan 2012 17:15:13 +0100
changeset 2694 5620f0a2fcc8
parent 2689 0c20a2edaac8
child 2702 7f34bed72174
permissions -rw-r--r--
fixed: #savePreferencesOn:

"
 COPYRIGHT (c) 2011 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' }"

AbstractSourceCodeManager subclass:#DataBaseSourceCodeManager
	instanceVariableNames:''
	classVariableNames:'Verbose RepositoryName ModuleDBs CachedDBHandles'
	poolDictionaries:''
	category:'System-SourceCodeManagement'
!

!DataBaseSourceCodeManager class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 2011 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
"
    A simple database sourceCodeManager, which saves versions in a relational db

    unfinished

    [author:]
        Claus Gittinger
"
! !

!DataBaseSourceCodeManager class methodsFor:'accessing'!

getRepositoryDBForModule:aModuleName
    "internal: used when accessing a source repository.
     Return the db-name for a particular module.
     If no specific db was defined for that module, return the value of
     the global (fallBack) repositoryDB.
     Nil is returned if no repository is available." 

    ModuleDBs isNil ifTrue:[^ RepositoryName].
    aModuleName isNil ifTrue:[^ RepositoryName].
    ^ ModuleDBs at:aModuleName ifAbsent:RepositoryName.

    "Modified: / 20-05-1998 / 16:30:12 / cg"
    "Created: / 26-12-2011 / 00:30:17 / cg"
!

knownModules
    "return the modules, we currently know"

    ModuleDBs isEmptyOrNil ifTrue:[^ #() ].
    ^ ModuleDBs keys

    "Modified: / 26-12-2011 / 00:48:51 / cg"
!

repositoryName
    "return the default repository"

    ^ RepositoryName

    "Created: / 26-12-2011 / 00:34:14 / cg"
!

repositoryName:aDBSpec
    "set the default repository"

    RepositoryName := aDBSpec.

    "Created: / 26-12-2011 / 01:13:59 / cg"
! !

!DataBaseSourceCodeManager class methodsFor:'private'!

dbHandleForModule:aModuleName
    |dbName handle|

    CachedDBHandles isNil ifTrue:[
        CachedDBHandles := WeakValueDictionary new.
    ].

    handle := CachedDBHandles at:aModuleName ifAbsent:nil.
    handle notNil ifTrue:[^ handle ].

    dbName := self getRepositoryDBForModule:aModuleName.
    dbName isNil ifTrue:[
        self error:'no database'.
        ^ nil
    ].

    handle := self openDB:dbName.
    handle isNil ifTrue:[
        self error:'no database'.
        ^ nil
    ].
    CachedDBHandles at:aModuleName put:handle.
    ^ handle.

    "Created: / 26-12-2011 / 00:59:49 / cg"
!

openDB:aDBName
    |idx dbType dbSpec|

    idx := aDBName indexOf:$:.
    dbType := aDBName copyTo:idx-1.
    dbSpec := aDBName copyFrom:idx+1.

    dbType = 'sqlite' ifTrue:[
        ^ self openSQLite:dbSpec
    ].
    dbType = 'odbc' ifTrue:[
        ^ self openODBC:dbSpec
    ].
    self error:'unsupported dbtype'

    "Created: / 26-12-2011 / 01:05:57 / cg"
!

openODBC:dbSpec
self halt.

    "Created: / 26-12-2011 / 01:06:41 / cg"
!

openSQLite:dbSpec
    |file con crsr tables row|

    file := dbSpec.
    con := SQLiteConnection fileNamed:file.
    con open.

    tables := OrderedCollection new.

    SqliteError handle:[:ex |
    ] do:[
        crsr := con executeQuery:'SELECT * FROM sqlite_master WHERE type=''table'';'. 
    ].
    crsr notNil ifTrue:[
        [ 
            crsr next.
            row := crsr rowAsDictionary.
            row notNil 
        ] whileTrue:[
            tables add:(row at:'name')
        ].
    ].

    (tables includes:'versions') ifTrue:[
        Transcript showCR:'table "versions" already present.'.
    ] ifFalse:[
        Transcript showCR:'creating table "versions"...'.
        con executeQuery:'CREATE table versions (versionId, name, packageId, author, timestamp);'.
    ].
    (tables includes:'packages') ifTrue:[
        Transcript showCR:'table "packages" already present.'.
    ] ifFalse:[
        Transcript showCR:'creating table "packages"...'.
        con executeQuery:'CREATE table packages (packageId, name);'.
    ].
    (tables includes:'classes') ifTrue:[
        Transcript showCR:'table "classes" already present.'.
    ] ifFalse:[
        Transcript showCR:'creating table "classes"...'.
        con executeQuery:'CREATE table classes (id, name, superclass, category, definition, packageId, versionId);'.
    ].
    (tables includes:'methods') ifTrue:[
        Transcript showCR:'table "methods" already present.'.
    ] ifFalse:[
        Transcript showCR:'creating table "methods"...'.
        con executeQuery:'CREATE table methods (id, className, selector, source, bytecode, packageId, versionId);'.
    ].

    ^ con

    "Created: / 26-12-2011 / 01:06:37 / cg"
! !

!DataBaseSourceCodeManager class methodsFor:'queries'!

defaultRepositoryName
    "/ '<db-type>:[<user>[.<password>]@][<host>]<db-name>[:<table-name>]'

    ^ 'sqlite:repository.db'

    "Created: / 22-12-2011 / 00:19:43 / cg"
!

enabled
    ^ true "/ false.

    "Created: / 21-12-2011 / 17:53:34 / cg"
!

isContainerBased
    "true, if the SCM uses some kind of source container (,v files).
     False, if it is like a database or filesystem."

    ^ false

    "Created: / 21-12-2011 / 18:53:55 / cg"
!

isResponsibleForPackage:aString
    ^ true.

    "Created: / 09-07-2011 / 14:32:20 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified (format): / 22-12-2011 / 00:05:39 / cg"
!

managerTypeName
    ^ 'DBRepository'

    "Created: / 16-08-2006 / 11:05:56 / cg"
!

nameOfVersionMethodForExtensions
    ^ #'extensionsVersion_DB'

    "Modified: / 22-12-2011 / 00:06:15 / cg"
!

nameOfVersionMethodInClasses
    ^ #'version_DB'

    "Modified: / 22-12-2011 / 00:06:21 / cg"
!

repositoryNameForPackage:packageId 
    "superclass AbstractSourceCodeManager class says that I am responsible to implement this method"

    ^ self getDBNameForModule:(packageId upTo:$: )

    "Created: / 21-12-2011 / 23:07:02 / cg"
!

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

    ^ DataBaseSourceCodeManagementSettingsAppl

    "Created: / 19-04-2011 / 12:43:29 / cg"
    "Modified: / 22-12-2011 / 00:06:53 / cg"
! !

!DataBaseSourceCodeManager class methodsFor:'saving'!

savePreferencesOn:aStream
    aStream nextPutLine:'DataBaseSourceCodeManager notNil ifTrue:['.
    self repositoryInfoPerModule notEmptyOrNil ifTrue:[
        aStream nextPutLine:'    DataBaseSourceCodeManager repositoryInfoPerModule:' , self repositoryInfoPerModule storeString , '.'.
    ].
    (Smalltalk at:#SourceCodeManager) == self ifTrue:[
        aStream nextPutLine:'    Smalltalk at:#SourceCodeManager put:DataBaseSourceCodeManager.'.
    ].
    aStream nextPutLine:'    DataBaseSourceCodeManager repositoryName:' , self repositoryName storeString , '.'.
    aStream nextPutLine:'].'.

    "Created: / 09-11-2006 / 15:09:25 / cg"
    "Modified: / 22-12-2011 / 00:48:25 / cg"
    "Modified: / 01-01-2012 / 17:02:25 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!DataBaseSourceCodeManager class methodsFor:'source code administration'!

checkForExistingContainer:fileName inModule:moduleName directory:dirName
    ^ self checkForExistingModule:moduleName directory:dirName

    "Created: / 21-12-2011 / 17:56:23 / cg"
!

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

    ^ true.
"/
"/    dir := self moduleDirectoryFor:moduleDir.
"/    ^ dir exists

    "Created: / 21-12-2011 / 18:37:28 / cg"
!

checkForExistingModule:moduleDir directory:packageDir
    "check for a package directory to be present; return true, if it does"

    |handle packagePresent name|

    handle := self dbHandleForModule:moduleDir.
    name := moduleDir,':',packageDir.

    packagePresent := false.
    handle
        withResultForQuery: ('select * from packages where name = "%1"' bindWith:name) 
        do:[:result |
            packagePresent := result numRows > 0.
        ].

    ^ packagePresent

    "Created: / 21-12-2011 / 18:03:33 / cg"
!

checkinClass:aClass fileName:classFileName directory:packageDir module:moduleDir source:sourceFile logMessage:logMessage force:force
    "Return true if ok, false if not."

    aClass definition.


    self halt.
"/    |targetDir newestRevision newRevision newFile packageMode filter outStream|
"/
"/    targetDir := self packageDirectoryForModule:moduleDir package:packageDir.
"/
"/    (targetDir filesMatching:(classFileName,'_*')) do:[:eachVersionFile |
"/        |versionString|
"/
"/        versionString := eachVersionFile copyFrom:(classFileName size + 2).
"/        (newestRevision isNil 
"/        or:[ self isRevision:versionString after:newestRevision ]) ifTrue:[
"/            newestRevision := versionString
"/        ].
"/    ].
"/
"/    newestRevision isNil ifTrue:[
"/        newRevision := '1'
"/    ] ifFalse:[
"/        newRevision := self revisionAfter:newestRevision
"/    ].
"/    newFile := (targetDir construct:classFileName,'_',newRevision printString).
"/
"/    self updateVersionMethodOf:aClass for:(self revisionStringFor:aClass inModule:moduleDir directory:packageDir container:classFileName revision:newRevision).
"/
"/    packageMode := self checkMethodPackagesOf:aClass.
"/    packageMode == #base ifTrue:[
"/        filter := [:mthd | mthd package = aClass package].
"/    ].
"/
"/    [
"/        outStream := newFile writeStream.
"/    ] on:FileStream openErrorSignal do:[:ex|
"/        self reportError:('fileout failed').
"/        ^ false
"/    ].
"/
"/    Method flushSourceStreamCache.
"/    Class fileOutErrorSignal handle:[:ex |
"/        outStream close.
"/        newFile delete.
"/        self reportError:('fileout failed (',ex description,')').
"/        ^ false
"/    ] do:[
"/        self 
"/            fileOutSourceCodeOf:aClass 
"/            on:outStream 
"/            withTimeStamp:false 
"/            withInitialize:true 
"/            withDefinition:true
"/            methodFilter:filter.
"/    ].
"/    outStream close.
"/
"/    newFile exists ifFalse:[
"/        self reportError:'fileout failed'.
"/        ^ false.
"/    ].
"/
"/    ^ true
"/
"/

    "Created: / 21-12-2011 / 19:01:07 / cg"
!

createModule:moduleDir
    "create a module directory"

    self halt.
"/    |dir|
"/
"/    dir := self moduleDirectoryFor:moduleDir.
"/    dir recursiveMakeDirectory.
"/    ^ dir exists.

    "Created: / 21-12-2011 / 18:38:22 / cg"
!

createModule:moduleDir directory:packageDir
    "create a package directory"

    |handle id name|

    id := UUID new.
    name := (moduleDir,':',packageDir).

    handle := self dbHandleForModule:moduleDir.
    handle
        withResultForQuery: ('insert into packages values (''%1'', ''%2'')' bindWith:id with:name) 
        do:[:result |
        ].

    self halt.
"/    |dir|
"/
"/    dir := self packageDirectoryForModule:moduleDir package:packageDir.
"/    dir recursiveMakeDirectory.
"/    ^ dir exists.

    "Created: / 21-12-2011 / 18:44:20 / cg"
!

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

    ^ self 
        revisionStringFor:aClass 
        inModule:moduleDir 
        directory:packageDir 
        container:fileName 
        revision:'1'

    "Created: / 21-12-2011 / 18:14:03 / cg"
!

revisionInfoFromString:aString 
    "{ Pragma: +optSpace }"

    "return a VersionInfo object filled with revision info.
     This extracts the relevant info from aString."

    |info path version user ts timeStamp idx1 idx2|

    "/ 'Path: stx/libbasic/Array.st, Version: 123, User: cg, Time: 2011-12-21T21:03:08.826'

    idx1 := aString indexOfSubCollection:'Path: '.
    idx1 ~~ 0 ifTrue:[
        idx1 := idx1 + 'Path: ' size.
        idx2 := aString indexOfSubCollection:', ' startingAt:idx1.
        path := aString copyFrom:idx1 to:idx2-1.     
    ].
    idx1 := aString indexOfSubCollection:'Version: '.
    idx1 ~~ 0 ifTrue:[
        idx1 := idx1 + 'Version: ' size.
        idx2 := aString indexOfSubCollection:', ' startingAt:idx1.
        idx2 == 0 ifTrue:[
            version := aString copyFrom:idx1     
        ] ifFalse:[
            version := aString copyFrom:idx1 to:idx2-1.     
        ].
    ].
    idx1 := aString indexOfSubCollection:'User: '.
    idx1 ~~ 0 ifTrue:[
        idx1 := idx1 + 'User: ' size.
        idx2 := aString indexOfSubCollection:', ' startingAt:idx1.
        idx2 == 0 ifTrue:[
            user := aString copyFrom:idx1     
        ] ifFalse:[
            user := aString copyFrom:idx1 to:idx2-1.     
        ].
    ].
    idx1 := aString indexOfSubCollection:'Time: '.
    idx1 ~~ 0 ifTrue:[
        idx1 := idx1 + 'Time: ' size.
        idx2 := aString indexOfSubCollection:', ' startingAt:idx1.
        idx2 == 0 ifTrue:[
            ts := aString copyFrom:idx1     
        ] ifFalse:[
            ts := aString copyFrom:idx1 to:idx2-1.     
        ].
        timeStamp := Timestamp readIso8601FormatFrom:ts
    ].

    info := VersionInfo new.
    path notNil ifTrue:[ info fileName:(path asFilename baseName) ].
    info revision:version.
    user notNil ifTrue:[ info user:user ].
    timeStamp notNil ifTrue:[ info timeStamp:timeStamp ].
    ^ info

    "
     self revisionInfoFromString:'Path: stx/libbasic/Array.st, Version: 123, User: cg, Time: 2011-12-21T21:03:08.826' 
    "

    "Created: / 21-12-2011 / 14:50:12 / cg"
!

revisionLogOf:clsOrNil fromRevision:rev1OrNil toRevision:rev2OrNil numberOfRevisions:limitOrNil fileName:classFileName directory:packageDir module:moduleDir 
    "Return true if ok, false if not."

    self halt.
"/    |info log targetDir count newestRevision|
"/
"/    targetDir := self packageDirectoryForModule:moduleDir package:packageDir.
"/    targetDir exists ifFalse:[^ nil ].
"/
"/    info := IdentityDictionary new.
"/    log := OrderedCollection new.
"/    count := 0.
"/
"/    (targetDir filesMatching:(classFileName,'_*')) do:[:eachVersionFile |
"/        |versionString cs versionChange info|
"/
"/        versionString := eachVersionFile copyFrom:(classFileName size + 2).
"/        count := count + 1.
"/        (newestRevision isNil 
"/        or:[ self isRevision:versionString after:newestRevision ]) ifTrue:[
"/            newestRevision := versionString
"/        ].
"/
"/        (rev1OrNil isNil 
"/            or:[ rev1OrNil = 0
"/            or:[ versionString = rev1OrNil
"/            or:[ self isRevision:versionString after:rev1OrNil ]]])
"/        ifTrue:[
"/            (rev2OrNil isNil 
"/                or:[ rev2OrNil = 0
"/                or:[ versionString = rev2OrNil
"/                or:[ self isRevision:rev2OrNil after:versionString ]]])
"/            ifTrue:[
"/                (limitOrNil isNil
"/                or:[ log size < limitOrNil ])
"/                ifTrue:[
"/                    cs := ChangeSet fromFile:(targetDir construct:eachVersionFile).
"/                    versionChange := cs detect:[:chg | chg isMethodChange
"/                                                       and:[chg selector = self nameOfVersionMethodInClasses]]
"/                                        ifNone:nil.
"/                    versionChange notNil ifTrue:[
"/                        info := self revisionInfoFromString:versionChange source.
"/                    ] ifFalse:[
"/                        info := VersionInfo new.
"/                    ].
"/
"/                    info revision:versionString.
"/                    log add:info.
"/                ]
"/            ].
"/        ].
"/    ].
"/    log sort:[:a :b | self isRevision:b revision after:a revision].
"/
"/    info at:#revisions put:log.
"/    info at:#numberOfRevisions put:count.
"/    info at:#newestRevision put:newestRevision.
"/
"/    ^ info
"/
"/

    "Created: / 21-12-2011 / 20:39:31 / cg"
!

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

    ^ 'Path: %1/%2/%3, Version: %4, User: %5, Time: %6'
        bindWith:moduleDir
        with:packageDir
        with:fileName
        with:revisionString
        with:(OperatingSystem getLoginName)
        with:(Timestamp now printStringIso8601Format)

    "
     self revisionStringFor:Array inModule:'stx' directory:'libbasic' container:'Array.st' revision:'123'
    "

    "Created: / 21-12-2011 / 19:33:33 / cg"
!

streamForClass:aClass fileName:classFileName revision:revision directory:packageDir module:moduleDir cache:doCache
    self halt.
"/    |targetDir oldFile|
"/
"/    targetDir := self packageDirectoryForModule:moduleDir package:packageDir.
"/    oldFile := (targetDir construct:classFileName,'_',revision).
"/    ^ oldFile readStream

    "Created: / 21-12-2011 / 20:49:01 / cg"
! !

!DataBaseSourceCodeManager class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libbasic3/DataBaseSourceCodeManager.st,v 1.5 2012-01-01 16:15:13 vrany Exp $'
!

version_CVS
    ^ '$Header: /cvs/stx/stx/libbasic3/DataBaseSourceCodeManager.st,v 1.5 2012-01-01 16:15:13 vrany Exp $'
! !