
!"Pocket Smalltalk fileout - 11:23:46 AM, Tuesday, February 09, 1999"!


!Object constantsFor: 'Data Manager'!

dmHdrAddrReadOnly 2! 
dmHdrAttrAppInfoDirty 4! 
dmHdrAttrBackup 8! 
dmHdrAttrOKToInstallNewer 16! 
dmHdrAttrOpen -1! 
dmHdrAttrResDB 1! 
dmHdrAttrResetAfterInstall 32! 
dmModeExclusive 8! 
dmModeLeaveOpen 4! 
dmModeReadOnly 1! 
dmModeReadWrite 3! 
dmModeShowSecret 16! 
dmModeWrite 2! !

Object subclass: #Database
	instanceVariableNames: 'dmOpenRef localID'
	classVariableNames: ''!

Database subclass: #ResourceDatabase
	instanceVariableNames: ''
	classVariableNames: ''!

!Database methodsFor: 'utility'!

close
	dmOpenRef ifNotNil: [
		SYSTRAP DmCloseDatabase: dmOpenRef.
		dmOpenRef := nil].!

removeSecretRecords
	SYSTRAP DmRemoveSecretRecords: dmOpenRef.! !


!Database methodsFor: 'predicates'!

isResourceDatabase
	^false.! !


!Database methodsFor: 'record access'!

recordCount
	"Record indexes are 0..recordCount-1"
	^SYSTRAP DmNumRecords: dmOpenRef.!

recordAsBytes: index
	| handle ptr bytes |
	handle := self queryRecord: index.
	handle isNull ifTrue: [^nil].
	bytes := ByteArray new: handle handleSize.
	ptr := handle lock.
	SYSTRAP MemMove: bytes from: ptr bytes: bytes basicSize.
	handle unlock.
	^bytes.!

recordAsString: index
	| bytes |
	bytes := self recordAsBytes: index.
	^bytes isNil
		ifTrue: [nil]
		ifFalse: [bytes basicChangeClassTo: String].!

insertRecord: bytes at: index
	"Answers the index of the new record."
	| handle ptr recordIndex |
	handle := self newRecordAt: index ofSize: bytes basicSize.
	"Hack - we know the record's 'actual' index is in PadBuffer."
	recordIndex := PadBuffer wordAt: 0.
	ptr := handle lock.
	SYSTRAP
		DmWrite: ptr
		offset: 0
		from: bytes
		bytes: bytes basicSize.
	handle unlock.
	self releaseRecord: recordIndex dirty: true.
	^recordIndex.!

removeRecord: index
	SYSTRAP DmRemoveRecord: dmOpenRef index: index.!

deleteRecord: index
	SYSTRAP DmDeleteRecord: dmOpenRef index: index.!

archiveRecord: index
	SYSTRAP DmArchiveRecord: dmOpenRef index: index.!

moveRecord: oldIndex to: newIndex
	SYSTRAP
		DmMoveRecord: dmOpenRef
		from: oldIndex
		to: newIndex.!

appendRecord: bytes
	"Answers the index of the new record.  The record is added to the end of the database."
	^self insertRecord: bytes at: self recordCount + 1.!

write: bytes intoRecord: recordIndex
	"bytes can be a String or ByteArray (or another byte indexable object)."
	| handle ptr |
	"Make room for new contents"
	(self resizeRecord: recordIndex to: bytes basicSize)
		isNull ifTrue: [^nil].
	handle := self getRecord: recordIndex.
	ptr := handle lock.
	SYSTRAP
		DmWrite: ptr
		offset: 0
		from: bytes
		bytes: bytes basicSize.
	handle unlock.
	self releaseRecord: recordIndex dirty: true.! !


!Database methodsFor: 'initialization'!

dmOpenRef: pointer
	dmOpenRef := pointer.!

localID: newLocalID
	localID := newLocalID.! !


!Database methodsFor: 'low-level access'!

queryRecord: index
	"Answer a handle to the record without setting the busy bit."
	"Answer a null pointer if the index is out of range or deleted."
	^SYSTRAP DmQueryRecord: dmOpenRef index: index.!

releaseRecord: index dirty: dirty
	SYSTRAP
		DmReleaseRecord: dmOpenRef
		index: index
		dirty: dirty asInteger.!

getRecord: index
	"Answer a handle to the record and mark it as busy."
	"The record must be released with #releaseRecord:dirty:"
	"Answer a null pointer if the index is out of range or deleted."
	^SYSTRAP DmGetRecord: dmOpenRef index: index.!

resizeRecord: index to: newSize
	^SYSTRAP
		DmResizeRecord: dmOpenRef
		index: index
		newSize: newSize.!

newRecordAt: index ofSize: size
	"Answers the handle for the new record.  The record is initially marked as busy."
	PadBuffer wordAt: 0 put: index.
	^SYSTRAP
		DmNewRecord: dmOpenRef
		atP: PadBuffer
		size: size.! !


!Database class methodsFor: 'opening'!

openID: localID mode: mode
	"Answers nil if the database could not be opened."
	"mode is a logical OR of one or more ##dmMode constants."
	| ref |
	ref := SYSTRAP
		DmOpenDatabase: 0
		localID: localID
		mode: mode.
	^ref isNull
		ifTrue: [nil]
		ifFalse: [self newForDmOpenRef: ref].!

openByType: databaseType creatorID: creatorID mode: mode
	| dbType dbCreatorID ref |
	dbType := self convertLongWord: databaseType.
	dbCreatorID := self convertLongWord: creatorID.
	(dbType isNil or: [dbCreatorID isNil]) ifTrue: [^nil].
	ref := SYSTRAP
		DmOpenDatabaseByTypeCreator: dbType
		creatorID: dbCreatorID
		mode: mode.
	^ref isNull
		ifTrue: [nil]
		ifFalse: [self newForDmOpenRef: ref].!

open: databaseName mode: mode
	"Answers nil if the database could not be opened."
	"mode is a logical OR of one or more ##dmMode constants."
	| localID |
	localID := self findDatabaseIDByName: databaseName.
	^localID isNil
		ifTrue: [nil]
		ifFalse: [self openID: localID mode: mode].! !


!Database class methodsFor: 'private'!

convertLongWord: value
	"Convert 4-element Strings and ByteArrays to integers."
	(value isBytes and: [value basicSize == 4])
		ifTrue: [^Integer fromBytes: value].
	^(value isKindOf: Integer)
		ifTrue: [value]
		ifFalse: [nil].!

newForDmOpenRef: dmOpenRef
	| info databaseClass |
	info := self getOpenDBInfo: dmOpenRef.
	databaseClass := (info at: 5)
		ifTrue: [ResourceDatabase]
		ifFalse: [Database].
	^self new
		dmOpenRef: dmOpenRef;
		localID: (info at: 1).!

getDBInfo: localID
	"Answer an Array with the following elements:
		1:	String with the database name
		2:	attributes value
		3:	creation date (Integer)
		4:	modification date (Integer)
		5:	backup date (Integer)
		6:	modification number
		7:	type ID (Integer)
		8:	creator ID (Integer)"
	| pad null array |
	pad := PadBuffer.
	null := CPointer null.
	SYSTRAP
		DmDatabaseInfo: 0
		localID: localID
		nameP: pad
		attributesP: (pad offsetBy: 32)
		versionP: null
		crDateP: (pad offsetBy: 34)
		modDateP: (pad offsetBy: 38)
		bckUpDateP: (pad offsetBy: 42)
		modNumP: (pad offsetBy: 46)
		appInfoIDP: null
		sortInfoIDP: null
		typeP: (pad offsetBy: 50)
		creatorP: (pad offsetBy: 54).
	array := Array new: 8.
	array
		at: 1 put: pad extractCString;
		at: 2 put: (pad wordAt: 32);
		at: 3 put: (pad dwordAt: 34);
		at: 4 put: (pad dwordAt: 38);
		at: 5 put: (pad dwordAt: 42);
		at: 6 put: (pad dwordAt: 46);
		at: 7 put: (pad dwordAt: 50);
		at: 8 put: (pad dwordAt: 54).
	^array.!

getOpenDBInfo: dmOpenRef
	"Answer an Array with the following elements:
		1:	LocalID for the database
		2:	openCount
		3:	mode
		4:	card number
		5:	is resource database (Boolean)"
	| pad array |
	pad := PadBuffer.
	SYSTRAP
		DmOpenDatabaseInfo: dmOpenRef
		localIDP: pad
		openCountP: (pad offsetBy: 4)
		modeP: (pad offsetBy: 6)
		cardNoP: (pad offsetBy: 8)
		resDBP: (pad offsetBy: 10).
	array := Array new: 5.
	array
		at: 1 put: (pad dwordAt: 0);
		at: 2 put: (pad wordAt: 4);
		at: 3 put: (pad wordAt: 6);
		at: 4 put: (pad wordAt: 8);
		at: 5 put: (pad wordAt: 10) asBoolean.
	^array.! !


!Database class methodsFor: 'utility'!

findDatabaseIDByIndex: index
	"Answer the local ID of the database with the given index, or nil if an invalid index is passed."
	| localID |
	localID := SYSTRAP DmGetDatabase: 0 index: index.
	^localID == 0
		ifTrue: [nil]
		ifFalse: [localID].!

deleteDatabaseID: localID
	SYSTRAP DmDeleteDatabase: 0 localID: localID.!

createDatabaseNamed: databaseName
creatorID: creatorID
type: type
isResourceDB: isResourceDB
	"Answer whether the creation was successful."
	| namePtr err creatorVal typeVal |
	creatorVal := self convertLongWord: creatorID.
	typeVal := self convertLongWord: type.
	(creatorVal isNil or: [typeVal isNil]) ifTrue: [^self].
	namePtr := databaseName copyToHeap: PadBuffer.
	err := SYSTRAP
		DmCreateDatabase: 0
		name: namePtr
		creatorID: creatorVal
		type: typeVal
		resDB: isResourceDB asInteger.
	^err == 0.!

findDatabaseIDByName: databaseName
	"Answer the local ID of the database with the given name, or nil if not found."
	| localID str |
	str := databaseName copyToHeap: PadBuffer.
	localID := SYSTRAP DmFindDatabase: 0 name: str.
	^localID == 0
		ifTrue: [nil]
		ifFalse: [localID].! !


!ResourceDatabase methodsFor: 'accessing'!

recordCount
	^SYSTRAP DmNumResources: dmOpenRef.! !


!ResourceDatabase methodsFor: 'predicates'!

isResourceDatabase
	^true.! !


