SDSU CS 535 Object-Oriented Programming
Spring Semester, 2003
Saving Data in Files
Previous    Lecture Notes Index        
© 2003, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 29-Apr-03

Contents of Doc 20, Saving Data in Files



References


VisualWorks Developer’s Guide, Chapter 21 Binary Object Files

OmniBase documentation, http://www.gorisek.com/homepage/index.html


Doc 20, Saving Data in Files Slide # 2

Saving Data in Files


Some Issues


Doc 20, Saving Data in Files Slide # 3
Handling changes in class of a saved object

Save an object or data in an object to a file

Modify the class by:


Read the data in the file

Recreate the object

What happens?


In Smalltalk adding/removing methods does not affect recreating the object

Doc 20, Saving Data in Files Slide # 4
Customer Class Used in Examples

Smalltalk defineClass: #Customer
   superclass: #{Core.Object}
   indexedType: #none
   private: false
   instanceVariableNames: 'name phone id '
   classInstanceVariableNames: 'nextId '
   imports: ''
   category: 'Course-GUI-Examples'

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!


Customer class methods

name: nameString phone: phoneString
   ^self new 
      name: nameString;
      phone: phoneString;
      id: self nextId

nextId
   nextId ifNil: [nextId := 0].
   ^nextId := nextId + 1.




Doc 20, Saving Data in Files Slide # 5
Instance Methods

= aCustomer
   ^(name = aCustomer name) & 
   (phone = aCustomer phone) & 
   (id = aCustomer id)

id
   ^id

id: anInteger
   id := anInteger

name
   ^name

name: aString
   name := aString

objectTerminator
   ^Character cr

phone
   ^phone

phone: aString
   phone := aString



Doc 20, Saving Data in Files Slide # 6

Simple Text Files


Let an Object know how to save/restore itself

Convert instance variables to strings

Use characters to separate instance variables

Saving
Customer>>saveOn: aStream 
   aStream
      nextPutAll: name;
      nextPut: self fieldSeparator;
      nextPutAll: phone;
      nextPut: self fieldSeparator;
      nextPutAll: id printString;
      nextPut: self objectTerminator

Customer>>objectTerminator
   ^Character cr

Customer>>fieldSeparator
   ^$,
Restoring
Customer class>>readFrom: aStream
   ^super new readFrom: aStream

Customer>>readFrom: aStream
   name := aStream upTo: self fieldSeparator.
   phone := aStream upTo: self fieldSeparator.
   id := (aStream upTo: self objectTerminator) asNumber


Doc 20, Saving Data in Files Slide # 7
Sample UseSaving
   | out customers dataFile |
   dataFile := 'dataFile.txt' asFilename.
   out := dataFile writeStream.
   customers := (OrderedCollection new)
      add: (Customer name: 'roger' phone: '1234567');
      add: (Customer name: 'pete' phone: '1111111');
      add: (Customer name: 'jose' phone: '2222222');
      yourself.
   customers do: [:each | each saveOn: out].
   out close.

Restoring
   | customers in dataFile |
   dataFile := 'dataFile.txt' asFilename.
   in := dataFile readStream.
   customers := OrderedCollection new.
   [in atEnd] whileFalse: [customers add: (Customer readFrom: in)].

dataFile.txt Contents
roger,1234567,34
pete,1111111,35
jose,2222222,36


Doc 20, Saving Data in Files Slide # 8
Issues

What characters to use to separate instance variables?
What character to use to end an object data?
How to handle nested objects?
What happens when you add/remove instance fields to the class?

Properly selected separators allow other programs to manipulate data files


Doc 20, Saving Data in Files Slide # 9

StoreString – Saving Objects in ASCII


storeString
   "Answer a String representation of the receiver from which 
   the receiver can be reconstructed."

Automatically serializes objects into strings

This is appropriate only for smaller simpler objects.

Can handle nested objects

Cannot handle circular references of objects.

Uses order of instance variables, can handle

Cannot handle

Calls storeOn: to perform work




Doc 20, Saving Data in Files Slide # 10
Saving
   | out customer dataFile |
   dataFile := 'dataFile.txt' asFilename.
   out := dataFile writeStream.
   customer := Customer name: 'roger' phone: '1234567'.
   out 
      nextPutAll: customer storeString;
      close

Restoring
   |  contents customer dataFile |
   dataFile := 'dataFile.txt' asFilename.
   contents := dataFile contentsOfEntireFile.
   customer :=Compiler evaluate: contents.
   customer inspect

dataFile.txt Contents
(Customer basicNew instVarAt: 1 put: 'roger'; instVarAt: 2 put: '1234567'; instVarAt: 3 put: 37; yourself)

Doc 20, Saving Data in Files Slide # 11
storeString Nested Object Example

Saving
   | out customers dataFile |
   dataFile := 'dataFile.txt' asFilename.
   out := dataFile writeStream.
   customers := (OrderedCollection new)
      add: (Customer name: 'roger' phone: '1234567');
      add: (Customer name: 'pete' phone: '1111111');
      add: (Customer name: 'jose' phone: '2222222');
      yourself.
   out 
      nextPutAll: customers storeString;
      close

Restoring
   |  contents customers dataFile |
   dataFile := 'dataFile.txt' asFilename.
   contents := dataFile contentsOfEntireFile.
   customers :=Compiler evaluate: contents.

dataFile.txt Contents
((Core.OrderedCollection new) add: (Customer basicNew instVarAt: 1 put: 'roger'; instVarAt: 2 put: '1234567'; instVarAt: 3 put: 39; yourself); add: (Customer basicNew instVarAt: 1 put: 'pete'; instVarAt: 2 put: '1111111'; instVarAt: 3 put: 40; yourself); add: (Customer basicNew instVarAt: 1 put: 'jose'; instVarAt: 2 put: '2222222'; instVarAt: 3 put: 41; yourself); yourself)

Doc 20, Saving Data in Files Slide # 12

BOSS – Binary Object Storage System


Stores objects in binary

Handles nested objects

Handles circular references of objects

Very sensitive to changes in Class

You must provide code to convert between old and new format


Need to load the BOSS parcel

In the Parcel Manager it is located in Application development

See Chapter 21 Binary Object Files in VisualWorks Developer’s Guide

Doc 20, Saving Data in Files Slide # 13
Storing
   | bos customers dataFile |
   dataFile := 'dataFile.txt' asFilename.
   bos  := BinaryObjectStorage onNew: dataFile writeStream.
   customers := (OrderedCollection new)
      add: (Customer name: 'roger' phone: '1234567');
      add: (Customer name: 'pete' phone: '1111111');
      add: (Customer name: 'jose' phone: '2222222');
      yourself.
   [bos nextPutAll: customers] ensure: [bos close]


Restoring
   |  customers dataFile |
   dataFile := 'dataFile.txt' asFilename.
   bos  := BinaryObjectStorage onOldNoScan: dataFile readStream.
   [customers := bos contents] ensure: [bos close].

Searching the file
   |  nextCustomer dataFile |
   dataFile := 'dataFile.txt' asFilename.
   bos  := BinaryObjectStorage onOldNoScan: dataFile readStream.
   [[bos atEnd]
      whileFalse: 
         [nextCustomer := bos next.
         nextCustomer name = 'pete' ifTrue:[^nextCustomer]]] 
   ensure: [bos close]

Doc 20, Saving Data in Files Slide # 14

XML Configuration Files


Download from main course page

Stores objects using XML

Handles nested objects

Handles circular references of objects

Very tolerant of changes in class of stored objects

Uses name of instance variable to restore object, so can



Note the full name of the class is Tools.XMLConfigFileSupport.XMLConfigFile

Doc 20, Saving Data in Files Slide # 15
Saving Multiple Objects

   |  xmlFile |
   xmlFile :=XMLConfigFile filename: 'dataFile.txt'.
   xmlFile 
      saveObject: (Customer name: 'roger' phone: '1234567');
      saveObject: (Customer name: 'pete' phone: '1111111');
      saveObject: (Customer name: 'jose' phone: '2222222');
      saveConfiguration.

Restoring
   |  xmlFile objects customer1 customer2  customer3 |
   xmlFile :=XMLConfigFile filename: 'dataFile.txt'.
   objects :=xmlFile loadConfiguration.
   customer1 := objects first.
   customer2 := objects at: 2.
   customer3 := objects at: 3

Doc 20, Saving Data in Files Slide # 16
Saving a Collection

   |  xmlFile |
   xmlFile :=XMLConfigFile filename: 'dataFile.txt'.
   customers := (OrderedCollection new)
      add: (Customer name: 'roger' phone: '1234567');
      add: (Customer name: 'pete' phone: '1111111');
      add: (Customer name: 'jose' phone: '2222222');
      yourself.
   xmlFile 
      saveObject: customers;
      saveConfiguration.

Restoring
   |  xmlFile |
   xmlFile :=XMLConfigFile filename: 'dataFile.txt'.
   objects :=xmlFile loadConfiguration.
   customers := objects first.

Doc 20, Saving Data in Files Slide # 17

OmniBase – Object Database


Handles nested objects

Handles circular references of objects

Very tolerant of changes in Classes of stored objects.

Can search for an object

Edit and save one object with having to resave all objects

Handles concurrent access

Supports transactions with rollback

Do not need separate database process

Download educational version at: http://www.gorisek.com/homepage/index.html


Doc 20, Saving Data in Files Slide # 18
Creating

   |  database |
   database :=OmniBase createOn: 'data'.
   database close.

You can create and use the database before closing
Creating is done once, so I do it here to get to over with

Simple adding to Database

   |  database |
   database :=OmniBase openOn: 'data'.
   [ OmniBase root
      at: 'test'
      put: (OrderedCollection newPersistent
         add: 'string object';
         add: 1;
         add: Date today;
         yourself) ] evaluateAndCommitIn: db newTransaction.

Accessing

   |  database date |
   database :=OmniBase openOn: 'data'.
   [ date:= (OmniBase root at: 'test') last ] evaluateIn: database newTransaction.
   ^date


Doc 20, Saving Data in Files Slide # 19
Modifying

Modifying the collect at test

   |  database test |
   database :=OmniBase openOn: 'data'.
   [ test:=  (OmniBase root at: 'test'). 
   test add: 'Hi Mom'.
   test markDirty] evaluateAndCommitIn: database newTransaction.
   database close.
Don’t have to resave all objects in the file


Object needs to be persistent and marked dirty


Doc 20, Saving Data in Files Slide # 20
Customer ExampleAdding the Customers
   |  database customers |
   database :=OmniBase openOn: 'data'.
   customers := (OrderedCollection new)
      add: (Customer name: 'roger' phone: '1234567');
      add: (Customer name: 'pete' phone: '1111111');
      add: (Customer name: 'jose' phone: '2222222');
      yourself.
   [customers makePersistent.
   customers do: [:each | each makePersistent]. 
   OmniBase root
      at: 'customers'
      put: customers ] evaluateAndCommitIn: database newTransaction.
   database close.

Accessing and Modifying a Customer

   |  database customers toChange  |
   database :=OmniBase openOn: 'data'.
   [customers :=  OmniBase root at: 'customers'. 
   toChange := customers detect: [:each | each name = 'pete'].
   toChange phone: '9999999'.
   toChange markDirty]
       evaluateAndCommitIn: database newTransaction.
   database close.

Copyright ©, All rights reserved.
2003 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
OpenContent license defines the copyright on this document.

Previous    visitors since 29-Apr-03