Anda di halaman 1dari 190

The Minimum You Need to Know

Abo
ut
aandx
Ba
About
utJav
ava
ndxBa
BasseJ

RolandHughes

LogikalSolutions

Copyright2010byRolandHughes
Allrightsreserved
PrintedandboundintheUnitedStatesofAmerica
ISBN13 9780982358030
ThisbookwaspublishedbyLogikalSolutionsfortheauthor.NeitherLogikalSolutionsnortheauthorshallbe
heldresponsibleforanydamage,claim,orexpenseincurredbyauserofthisbookandthecontentspresented
withinorprovidedfordownloadathttp://www.theminimumyouneedtoknow.com.

Thesetrademarksbelongtothefollowingcompanies:
Borland
BorlandSoftwareCorporation
Btrieve
BtrieveTechnologies,Inc.
CIndex/II
TrioSystemsLLC
Clipper
ComputerAssociates,Inc.
CodeBaseSoftware
SequiterInc.
CodeBase++
SequiterInc.
CommLib
GreenleafSoftware
Cygwin
RedHat,Inc.
DataBoss
KedwellSoftware
DataWindows
GreenleafSoftware
dBASE
dataBasedIntelligence,Inc.
DEC
DigitalEquipmentCorporation
DECBASIC
HewlettPackardCorporation
DECCOBOL
HewlettPackardCorporation
Foxbase
FoxSoftware
FoxPro
MicrosoftCorporation
FreeDOS
JimHall
GDB
GreenleafSoftware
HP
HewlettPackardCorporation
IBM
InternationalBusinessMachines,Inc.
Java
SunMicrosystems,Inc.
Kubuntu
CanonicalLtd.
Linux
LinusTorvals
LotusSymphony
InternationalBusinessMachines,Inc.
MAC
AppleInc.
MappppQuest
MapQuest,Inc.
MySQL
MySQLAB
Netware
Novell,Inc.
OpenVMS
HewlettPackardCorporation
OpenOffice
SunMicrosystems,Inc.
openSuSE
Novell,Inc.
ORACLE
OracleCorporation
OS/2
InternationalBusinessMachines,Inc.
Paradox
CorelCorporation
ProC
ProCCorp.
Quicken
IntuitInc.
RMS
HewlettPackardCorporation
RDB
OracleCorporation
SourceForge
SourceForge,Inc.
Ubuntu
CanonicalLtd.

Unix
VisualBasic
Watcom
Windows

ZincApplicationFramework

OpenGroup
MicrosoftCorporation
Sybase
MicrosoftCorporation
ProfessionalSoftwareAssociates,Inc.

Allothertrademarksinadvertentlymissingfromthislistaretrademarksoftheirrespectiveowners.Abesteffort
wasmadetoappropriatelycapitalizealltrademarkswhichwereknownatthetimeofthiswriting.Neitherthe
publishernortheauthorcanattesttotheaccuracyofanysuchinformationcontainedherein.Useofaterminthis
bookshouldnotbe regardedasaffectingthevalidityofanytrademarkorservicemark.

Ad
diti
Hughe
Add
itioona
nallBooksby
BooksbyRoland
ndH
hess
You can always find the latest information about this book series by visiting http://
www.theminimumyouneedtoknow.com. Information regarding upcoming and outofprint books
maybefoundbyvisiting http://www.logikalsolutions.com andclickingthe upcomingandoutof
printbooks link. Atthetimeofthiswriting,Logikal SolutionsandRolandHughes offer the
followingbookseitherinprintoraseBooks.
TheMinimumYouNeedtoKnowAboutLogictoWorkinIT
ISBN139780977086627
Pages:154
Coverslogic,flowcharting,andpseudocode.IfyouonlylearnedOOP,youreallyneed
toreadthisbookfirst.
TheMinimumYouNeedtoKnowToBeanOpenVMSApplicationDeveloper
ISBN139780977086603
Pages:795
IncludesCDROM
CoversDCL,logicals,symbols,commandprocedures,BASIC,COBOL,FORTRAN,C/
C++,Mysql,RDB,MMS,FMS,RMSindexedfiles,CMS,VMSPhone,VMSMAIL,
LSE,TPU,EDTandmanyothertopics.ThisbookwashandedoutbyHPatatechnical
bootcampbecausetheOpenVMSengineeringteamthoughtsohighlyofit.
TheMinimumYouNeedtoKnowAboutJavaonOpenVMS,Volume1
ISBN139780977086610
Pages:352
IncludesCDROM
CoversusingJavawithFMSandRMSindexedfiles.ThereisalotofJNIcoding.We
alsocovercallingOpenVMSlibraryroutines,buildingwithMMSandstoringsourcein
CMS.

TheMinimumYouNeedtoKnowAboutServiceOrientedArchitecture
ISBN139780977086665
Pages:370
TheNationalBestBooks2008AwardWinnerBusiness:Technology/Computer
CoversaccessingyourMySQL,RDB,andRMSindexedfiledatasilosviaJavaandport
servicesfromaLinuxorotherPCfrontend.Alsocoversdesignanddevelopmentof
ACMSbackendsystemsforguaranteedexecutionapplications.
InfiniteExposure
ISBN139780977086696
Pages:471
AnovelabouthowtheoffshoringofITjobsanddatacenterswillleadtothelargest
terroristattackthefreeworldhaseverseenandultimatelytonuclearwar.
Thereareanumberofreviewsofthisbookavailableonline.Thefirst18chaptersare
alsobeinggivenawayforfreeatBookHabit,ShortCovers,Sony'seBookstore,andmany
otherplaces.Ifyoucan'tdecideyoulikeitafterthefirst18chapters,Rolandreally
doesn'twanttodobusinesswithyou.

SourceCodeLicense
Thisbookisbeingofferedtothepublicfreely,asisthesourcecode.Pleaseleavecomments
aboutthesourceoforigininplacewhenincorporatinganyportionofthecodeintoyourownprojects
orproducts.
Usersofthesourcecodecontainedwithinthisbookagreetoholdharmlessboththeauthorand
thepublisherforanyerrors,omissions,losses,orotherfinancialconsequenceswhichresultfrom
theuseofsaidcode.Thissoftwareisprovidedas is withnowarrantyofanykindexpressedor
implied.
Visithttp://www.theminimumyouneedtoknow.comtofindadownloadlinkifyoudon't
want
toretypeorcutandpastecodefromthisbookintoyourowntexteditor.

TableofContents
Introduction......................................................................................................................................11
WhyThisBook?...................................................................................................................

11

WhyxBaseJ?.........................................................................................................................

13

ABriefHistoryofxBASE....................................................................................................

15

WhatisxBASE?...................................................................................................................

17

Limits,Restrictions,andGotchas.........................................................................................

24

Summary...............................................................................................................................

28

ReviewQuestions.................................................................................................................

28

Chapter1

...................................................................................................................................
29

1.1 OurEnvironment................................................................................................................

29

env1.......................................................................................................................................29
1.2 OpenorCreate?..................................................................................................................

30

1.3 Example1...........................................................................................................................

32

example1.java.......................................................................................................................32
1.4 ExceptionHandlingandExample1...................................................................................

37

1.5 rollie1.java..........................................................................................................................

39

rollie1.java............................................................................................................................39
...............................................................................................................................................48
testRollie1.java......................................................................................................................49
1.6 ProgrammingAssignment1...............................................................................................

49

1.7 SizeMatters........................................................................................................................

49

example5.java.......................................................................................................................49
1.8 ProgrammingAssignment2...............................................................................................

51

1.9 ExaminingaDBF...............................................................................................................

51

showMe.java.........................................................................................................................52
1.10ProgrammingAssignment3

...............................................................................................

61

1.11DescendingIndexesandIndexLifespan

............................................................................

62

doeHistory.java.....................................................................................................................63
testDoeHistory.java...............................................................................................................76
1.12ProgrammingAssignment4

...............................................................................................

82

1.13DeletingandPacking

.........................................................................................................

83

testpackDoeHistory.java.......................................................................................................84
1.14ProgrammingAssignment5

...............................................................................................

88

1.15DataIntegrity

......................................................................................................................

88

1.16ProgrammingAssignment6

...............................................................................................

89

1.17Summary

............................................................................................................................

90

1.18ReviewQuestions

...............................................................................................................

91

Chapter2

...................................................................................................................................
93

2.1 WhyThisExample?............................................................................................................

93

2.2 SupportingClasses............................................................................................................

102

MegaDBF.java....................................................................................................................102
StatElms.java...................................................................................................................... 106
StatDBF.java.......................................................................................................................107
MegaXDueElms.java..........................................................................................................112
.............................................................................................................................................113
DueSortCompare.java.........................................................................................................114
2.3 ThePanels.........................................................................................................................

115

MegaXbaseDuePanel.java.................................................................................................. 115
MegaXbaseBrowsePanel.java.............................................................................................124
MegaXbaseEntryPanel.java................................................................................................128
2.4 TheImportDialog.............................................................................................................

153

MegaXImport.java..............................................................................................................153
.............................................................................................................................................157
.............................................................................................................................................157
.............................................................................................................................................157
MegaXbase.java..................................................................................................................157
testMegaXbase.java............................................................................................................163
2.5 ProgrammingAssignment1..............................................................................................

164

2.6 ProgrammingAssignment2..............................................................................................

165

2.7 ProgrammingAssignment3..............................................................................................

165

2.8 ProgrammingAssignment4.............................................................................................

165

2.9 Summary...........................................................................................................................

165

2.10ReviewQuestions

.............................................................................................................

167

Chapter3

.................................................................................................................................
169

3.1 AuthoritativeResources....................................................................................................

169

3.2 TimestampsonReports.....................................................................................................

172

3.3 DoomedtoFailureandTooStupidtoKnow....................................................................

176

AppendixA....................................................................................................................................181
AnswerstoIntroductionReviewQuestions:......................................................................

181

AnswerstoChapter1ReviewQuestions...........................................................................

182

AnswerstoChapter2ReviewQuestions...........................................................................

185

Introduction
WhyThisBook?
IaskedmyselfthatsamequestioneverydaywhileIwaswritingit.WhyamIgoingtowrite
abookmuchlikemyotherbooksandgiveitawayforfree?ThesimpleansweristhatIhadtodo
alotoftheresearchanyway.IfIhavetodothatmuchresearch,thenIshouldputoutabook.
Giventhenarrownessofthemarketandthepropensityforpeopleinthatmarkettobelieveall
softwaredevelopersworkforfree,thebookwouldphysicallysellabouttwocopiesifIhadit
printed.(Lessthan1/10thof1%ofallLinuxusersactuallypayforanysoftwareortechnology
booktheyuse.)
Whatstartedmedownthispathwasasimplething.InordertomakeaWebsitereallywork,
afamilymemberneededtobeabletocalculatethe100miletruckingrateforthecommodity
beingsold.ThecommercialWebsitehadareallycoolfeaturewhereitwouldautomaticallysort
allbidswithin300milesbasedupontheperbushelprofitoncethetransportationcostsweretaken
out.Thepersonalreadyhadaprintedlistofthetruckingrates,sohowdifficultcoulditbe?
Somequestionsshouldneverbeaskedinlife.Wha tcouldgowrong? andH owdifficult
coulditbe? aretwowhichfallintothatcategory.Whenyouaskquestionslikethat,youtendto
getanswersyouwereunpreparedtohear.
InmyearlyDOSdays,IwouldhavesatdownandbeguncodingupaCprogram using
GreenleafDataWindowsandtheGreenleafDatabaselibrary.Ofcourse,backthen,wedidn'thave
theInternet,soIwouldhavehadtousetheGreenleafCommLibtodialouttosomeBBStoget
theDOE(DepartmentofEnergy)nationalaveragefuelprice.
DuringlaterDOSdays,butbeforeMicrosoftwroteataskswitchingGUIthatsatontopof
DOSandthatwasstartedbytypingWINattheC:>promptwhichtheyhadtheaudacitytocall
The WindowsOperatingSystem, IwouldhavereachedforaC/C++codegeneratorlikeProC
fromVestronix(laterProCCorp.)orDataBossfromKedwellSoftware. Neither program did
communications,but bothcouldbeusedtoquicklylayoutxBASEdatabases,generatingentry/
maintenancescreens,menus,and reportsin amatterof minutes. Youcouldcreateanentire
applicationthatusedjustafewfilesfordistribution,allofwhichcouldbecopiedintoasingle
directory,andtheuserwouldbehappy.

12

Chapter1Fundamentals

OnceWindowscameout,thingsgotugly.IdidalotofOS/2work,eventhoughnotmany
companiesorpeopleusedit.TheproblemwithOS/2wasthatIBMhadMicrosoftdevelopitand
Microsoftwasintentonensuring thatOS/2 would neverbeathreattoWindows. (Windows
wasn't
evenanactualoperatingsystemuntilmanyyearsafterOS/2cameout.)OnceIBMhadthe
bulkoftheMicrosoftcoderemovedfromit,OS/2becameanincrediblystableplatformwhich
managedmemorywell. IBMdidn't
manageitwell,saddlingdeveloperswithexpensivedevice
driver development tools that would only work with an increasingly hardtofind version of
MicrosoftC.
Mostofusdidcrossplatformdevelopmentinthosedays.IusedWatcomC/C++forDOS,
Windows,andOS/2development(nowOpenWatcomastheprojectisOpenSource).Itwaseasy
when you used the Zinc Application Framework for your GUI. There were a ton of cross
platform indexedfilelibraries. Greenleafsupportedalotofcompilersandplatformswith its
DatabaselibraryforxBASEfiles.Ofcourse,therewerealotofsharewareandcommercialBtree
type indexed file systems out there. These had the advantage of locking the user into your
services.Thesehadthedisadvantageoflockingtheuserintoyourservices.Theyweren't
widely
supportedbycommon tools likespreadsheetsandwordprocessors.TheoneIrememberusing
themostwasCIndex/IIfromTrioSystemsLLC.AsIrecallitwaswrittengenericallyenough
thatitactuallyworkedonDOS,MAC,Windows,andOS/2.Ofcourse,thatwasduringthebrief
periodinlifewhentheMetrowerksCodeWarriortoolsetsupportedtheMAC.
Inshort,fromthe80sthroughtheendofthe90swealwayshadsomewayofcreatinga
standaloneapplicationwithitsownindexedlocaldatastoragethatdidn't
requirelotsofother
thingstobeinstalled.WhenWindowsstartedgoingdownthepathofneedinglotsofotherstuff
was when you started seeing companies selling software to do nothing other than develop
installationprogramsforWindows.
Asanapplicationdeveloper whoisquitelonginthetooth,Idon't
wanttolinkwithDLLs,
sharedlibraries,installedimages,oranyotherthingwhichisexpectedtobeinstalledonthetarget
platform.Ihaveheardeveryjustificationforitknowntoman.Iwasthereandlistenedtopeople
slam my C program because their Visual Basic (VB) application took only 8K and looked
slicker than my application which consumed an entire floppy. I was also there to watch
productioncometoascreechinghaltwhenanewversionoftheVBruntimegotinstalledto
supportsomeothermission criticalapp onlytofindallpreviousappswerenowincompatible.
(ThemachinerunningmyCprogramwhichtookawholefloppycontinuedtokeepthebusinessit
supportedrunningwhilemuchscreamingandfingerpointingwasgoingonallaroundit.)

Chapter1Fundamentals

13

Whythisbook? BecausethepersondownloadingyourSourceForgeprojectorotherfree
pieceofsoftwaredoesn't
considerrecompilingaLinuxKernelafunthingtodoinhisorherfree
time.
Why this book? Because Mom and Dad shouldn'thave to take a course on MySQL
administrationjusttoentertheirexpensesandfiletheirtaxes.
Whythisbook?BecauseIhadtodoallofthisresearch,whichmeantIhadtotakealotof
notesanyway.
Whythisbook?BecauseOpenSourcelibrariesdon'tcomewithsquatfordocumentation.
WhyxBaseJ?
That'sagoodquestion. Partoftheanswerhasto dowiththehistoryIprovidedinthe
previoussection.Theotherparthastodowiththelanguagechosen.
Idon't
domuchPCprogramminganymore.IneededthisapplicationtorunonbothUbuntu
Linux andWindows. Thereisn't
agood OpenSourcecrossplatformGUIlibraryoutthere.
MostoftheLinuxGUIlibrariesrequirealotofstuffto be installedonaWindowsplatform
(usuallythebulkofCygwin)andthatrequireswritingsomekindofinstallationutility.Let's
just
saythattheOpenSourceinstallationgenerationtoolsforWindowshaven't
quitecaughtuptotheir
expensivecommercialcounterparts.Youdon't
reallywanttosaddleaWindowsmachinewhich
hasthebareminimumWindowsconfigurationwithsomethinglikeCygwinontopofit.
WhenIdiddoPCprogramming, IneverreallydidmuchwithTCP/IPcallsdirectly. IfI
magicallyfoundanOpenSourcecrossplatformGUIwhichdideverythingIneededonbothLinux
andWindows,IwasstillgoingtohavetofindacrossplatformTCP/IPlibrary.Letusnotforget
thatsome64bitLinuxdistroswon't
run32bitsoftwareandsome32bitsoftwarewon't
runon
64bitversionsofWindowsVista.ProgrammingthisinC/C++wasgoingtorequirealotmore
effortthanIwantedtoputintosomethingIwouldbasicallyhandoutfreeonceitwasworking
correctly.(YoumayalsohavenoticedIdidn't
evenmentionfindingalibrarywhichwouldwork
onWindows,Linux,andMAC.)
Java,whilenotmyfavoritelanguage,tendstobeinstalledonanymachinewhichconnectsto
theInternet.MostWindowsusersknowwheretogotodownloadandinstalltheJREwhichisn't
installedbydefaultforsomeversionsofWindows.FromwhatIhear,thepissingcontestisstill
goingonbetweenwhatisleftofBillGates'sEvilEmpireandwhatisleftofSun.
Java,whilenotmyfavoritelanguage,providesaGUIforalmosteveryplatformitrunson.

14

Chapter1Fundamentals

Java,whilenotmyfavoritelanguage,makesopeningaURLandparsingthroughthetextto
findcertaintokensprettysimpleifyouhappentoknowwhatclasstouse.
Java,whilenotmyfavoritelanguage,willnotcareiftheunderlyingoperatingsystemis32
or64bit.
MostmachineswhichuseabrowserandconnecttotheWebhavesomeformoftheJava
Runtime Environment (JRE) installed on them. This is true of current Linux, MAC, and
Windowsmachines.
Obviously I was going to have to develop this package with a language that wasn'tmy
favorite.
Theonlyquestionremainingwasdatastorage.WouldIforceMom,Dad,andAuntCarolto
enrollinaMySQLadministrationcourseeventhough theycanbarely answeremailandfind
MapQuestontheInternet,orwasIgoingtousesomethingselfcontained? Givenmyearlier
tirade,youknowIwantedtousesomethingselfcontainedjusttopreservemyownsanity.
Atthetimeofthiswriting,asearchonSourceForgeusing javaindexfile yieldsjustshyof
29,000projectsandasearchusingjava xbase yieldsjustshyof20,000projects.Granted,after
yougetseveralpagesintothesearchresults,thepercentageofrelevancydropsexponentially,but
therearestillalotofchoices.Btreetypeindexedfileswhichstoretheindexinthefilewiththe
datatendtobefarmorereliablefromadataintegritystandpoint.Allindexesarealwayskeptin
syncbythelibrary/engine.xBASEtypefilesstoretheindexesoffindifferentfiles.Youcanadd
alloftherecordsyouwanttoanxBASEdatafilewithouteverupdatinganindex.
Ican heartheheadscratchingnow. But ifthat's
true, whywouldyou use30yearold
xBASEtechnologyinsteadofaBtree? Becauseofthetools,child.OpenOfficecanopenaDBF
fileinaspreadsheettoletauserviewthedata.Ifanyofthesefilesbecomecorruptedthereare
literallyhundredsofxBASErepairtoolsoutthere.Ifauserdecidesheorshewantstoloadthe
dataintosomeotherdatabaseformatforanalysis,therearetoolsouttheretoexportxBASEinto
CSV(CommaSeparatedValue)fileswhichcaneasilybeimportedbymostrelationaldatabase
engines. Some relational database engines can directly import xBASE files. Nearly every
programminglanguageouttherehassomeformofxBASElibrary,orcancallonewritteninsome
otherlanguage. PerlevenhasanxBASElibrarythatI've
usedtoextractdatafromanexpense
trackingsystembefore.UnderLinux,thereisevenadbf_dumputility(dbfdumponOpenSuSE
forsomereason)whichwillletyoudumpaDBFfiletoaCSVinonecommand.
dbfdump /windows/D/net_f/xpns_$tax_year/payee.dbf > payee.csv

Chapter1Fundamentals

15

WhathappensifIuseoneofthosereallyfastBtreeorB+treelibrariesandtheuserneedsto
getthedataout?Suchuserscussmeprettyhardwhennoneoftheofficesuitesontheircomputer
canopenthefiletodoanexport.WhentheytrackmedownviatheWebandcallmyoffice,they
getdisappointedfindingoutIdon't
havetimetodropeverythingandflytotheirlocationtohelp
themfree ofcharge. Then theysaymy name, spit,andstartbadmouthing me allover the
Internet.Thatreallyhelpsmyconsultingbusiness.
NowthatwehavedeterminedthedatawillbestoredinanxBASEfileformat,weonlyhave
tochooseanOpenSourcexBASElibraryfor Java. IselectedxBaseJbecauseitusedtobea
commerciallibraryknownasXbaseJandwassoldbyBMTMicro.Theproducthassincebecome
anOpenSourceProjectwhichgetsperiodicimprovements.Thedeveloperactuallymonitorshis
SourceForgesupportforumandseemstobeactivelyaddingnewthingstothelibrary. Some
thingsdon't
workoutsowell,liketheDTDtoxBASEXMLparser,buttheattemptwasmade.
Someoneelseinthecommunitymightfinishit.
Pleasepayattentiontothethoughtprocessofthissection.Aseasonedsystemsanalystand/or
consultantgoesthroughexactlythisthoughtprocesswhenheorshetriestodesignasystem.You
lookatwhatisavailableonthetargetplatform,thenwalkbackwardstryingtoreducetheamount
ofpainyoufeelwhenyoucan't
changethetargetplatform.Icannotchangethecomputerspeople
have,northeirpersonalskilllevels.Ihavetodesignanapplicationbasedupontheabilityofthe
user,notmyabilitytobecreative,orthetoolsIwouldprefertouse.
ABriefHistoryofxBASE
TherearemanyvariationsinthecapitalizationofxBASE,whichisIguessfitting,sincethere
aremanyslightvariationsfortheactualfileformats.ThehistoryofxBASEisasordidtale,but
allversionsofxBASEinoneway oranother tracetheirrootsbackto the1970sandtheJet
PropulsionLaboratory.HereisthetaleasbestIcanremember.
PCswereoriginallyveryexpensive. Inthelate1970s youcouldbuyawell equipped
ChevroletCapriceClassic4doorsedanforjustover$4,000.Intheearly1980syoucouldbuya
dualfloppymonochromePCforaboutthesameamount.Whenclonevendorsenteredthemarket
youstartedseeingdualfloppyclonePCsforunder$2,000.ThehigherendPCsstartedadding
fullheight10MEGharddrivestojustifykeepingtheirpricesohigh.Eventually,youcouldgeta
clonePCwithawhopping20MEGharddrivefornearly$2000.

16

Chapter1Fundamentals

Oncethat$2000pricepointforaPCwithaharddrivewasachieved,thePCstartedgetting
pushedintotheworldofbusiness.Thefirstthingthebusinesseswantedtodowithitwaskeep
thingsinsortedorder. Theyheardfromkidsenrolledincomputerprogramming coursesthat
midrange andmainframecomputersusedalanguagecalledCOBOLwhichsupported indexed
filesthatcouldbeusedtostoreinvoices,payments,etc.,allinsortedorder,soinformationwas
quickly(fortheday)retrievable. Well,thePCdidn'thavethat,andbusinessusersneededit.
There was a noncommercial product called Vulcan written by Wayne Ratliff which kind of
answeredsomeofthoseneeds. AshtonTateeventuallyreleasedacommercialproductnamed
dBASEII.(TheyusedIIinsteadofItomaketheproductseemmorestable.I'm
notmakingthat
up.)
AshtonTatehadalotofsales,alotofmoney,alotofattitude,andalotoflawyers.Thisled
tothembelievingtheyhadtherightstoallthingsdBASE.Whenthecashcowstartedgivinglots
ofgreenmilktheclonevendorspiledintothefray. AshtonTateletloosewithablizzardof
lawsuitstryingtoshowitwasthemeanestdoginthejunkyard.Theclonevendorsquicklygot
aroundthedBASEtrademarkinfringementbycallingtheirfileformatsxBASE. (Somecalled
theirsXBase,othersXBase,etc.)
Times and public sentiment turned against AshtonTate. The people who spent many
hundredsofdollarsforthesetoolsandevenmoremoneyforsomeoftheruntimelicenseswhich
hadtobeinplaceonthemachinesbeforeapplicationswrittenwiththetoolwantedastandard.
WhentheywerefinallyfedupwithAshtonTateoroneoftheclones,theynaivelybelievedit
wouldbelikethoseoldCOBOLprograms,recompileandrun. Sillycustomers. Thiswasthe
peakofproprietarysoftware(theheightofwhichturnedouttobeMicrosoftWindows,which
even today is considered one of the most proprietary operating systems running on a PC
architecture),andtherewasnoincentiveforanyofthosereceivingruntimelicensefeestoagree
toastandard.Well,noincentiveuntilthebusinesscommunityasawholedeemedthefeesthey
chargedtoohigh.
Whenthepriceofaruntimelicensereachedhundreds,thebusinesscommunitycriedfoul.
Whenthememoryfootprintoftheruntimemeantyoucouldn't
loadnetworkdriversorother
applicationsin thatprecious 640KwindowaccessiblebyDOS,dirtylaundry gotairedrather
publicly.

Chapter1Fundamentals

17

VultureCapitalists,alwayssniffingthewindfordirtylaundryandviewingitasopportunity,
startedhurlingmoneyatsoftwaredeveloperswhosaidtheycouldwriteaCprogramminglibrary
which wouldlet other programmersaccessthesefileswithout requiring a runtime imageor
license. Theinitialpricetagforthoselibrariestendedtobequitehigh. Sincetherewereno
royaltypayments,thedevelopersandtheVultureCapitaliststhoughttheb est pricetheycould
offerwassomethingtotallingabouthalfofwhatthebigcorporationswerecurrentlypayingfor
development+runtimelicensefees. Forabriefperiodoftime,theywerecorrect. Thenthe
numberoftheselibrariesincreasedandthepricegotdowntounder$500each.Thecompanies
vendingproductswhichrequiredruntimelicensefeessawtheirrevenuestreamsevaporate.
Theevaporationwasagoodthingfortheindustry.ItallowedBorlandtopurchaseAshton
Tatein1991.PartofthepurchaseagreementappearstohavebeenthatAshtonTatedropallofits
lawsuits. After that committee ANSI/X3J19 was formed and began working on xBASE
standards.In1994BorlandendedupsellingthedBASEnameandproductlinetodBASEInc.
The standards committee accomplished little, despite all the major vendors participating.
Moreofthedatafileformats,values,andstructureswereexposedbyeachvendor,buteachofthe
vendors in the meetings wanted every other vendor to adopt its programming language and
methodsofdoingthingssoitwouldbethefirsttomarketwiththeindustrystandard.
TherearestillcommercialxBASEvendorsoutthere. MicrosoftownswhatwasFoxbase.
dBASEisstillsellingproductsandmigratingintoWebapplicationcreation.Mostofthereally
bignameproductsfromthelate80sarestillaround; theyjusthavedifferent owners. Sadly,
LotusApproachwasdroppedbyIBMandnotresurrectedwhenitcameoutwiththeSymphony
OfficeSuite.
IwillhazardaguessthatsomeoftheC/C++xBASEprogramminglibrariesfrommyDOS
daysarestillaroundandbeingsoldbysomeone. ThatwouldmakesensenowthatFreeDOSis
startingtogetafollowing.NotquiteasmuchsensegivenalloftheOpenSourceC/C++xBASE
librariesoutthere,buttheoldcommercialtoolshavealotmoretimeinthefield,andshould
thereforebemorestable.IknowthatGreenleafisbackinbusinessandyoucanprobablygeta
copyofGDB(GreenleafDatabase)fromthem;Ijustdon'tknowwhatplatformstheystillsupport.
ThereisalotofhistoryandfolkloresurroundingthehistoryofxBASE.Youcouldprobably
makeamovieoutofitliketheymadeamovieoutoftheriseofMicrosoftandApplecalled
Pirates ofSiliconValley in1999. Youcanpiecetogetherpartofthehistory,atleastfroma
compatibility standpoint, byobtaining acopy of The dBASELanguage Handbook written by
DavidM.KalmanandpublishedbyMicrotrendBooksin1989. Anotherworkwhichmightbe
worthy of your time is Xbase Cross Reference Handbook written by Sheldon M. Dunn and

18

Chapter1Fundamentals

publishedin1993bySybex,Inc.
For our purposes, you only need to know that back in the 1970s the Jet Propulsion
Laboratoryneededanindexedfilesystemtostoredataforvariousretrievalneeds. Whatthey
designedeventuallydevelopedmanyflavors,butallofthoseflavorsarenowlumpedunderthe
xBASEheadingbythegeneralpublic.
WhatisxBASE?

Note: This information is correct. You will find other information on the Web which
completelycontradictsportionsofthisinformation,anditwillalsobecorrect.Whatyouhaveto
takeintoaccountisthepointintimetheinformationreferences.Therearesomenewtoolsonthe
marketwhichclaimtheyarexBASEandhavenomaximumfilesize.Asyouwillseelater,thisis
not the original xBASE, which has a 1,000,000,000 byte file size limit, nor the later DOS/
WindowsxBASEproducts,whicheventuallyexpandedthemaximumfilesizeto2GB.xBASE
evolvedwiththeDOSPCfromthetimewhenwehaddualfloppysystemswhichwouldhaveat
least64KofRAM,butcouldhaveallthewayupto640K.Therewasatimewhenthelargest
harddriveyoucouldbuyforaPContheretailmarketwas20MB.
Note2:Alotofwellmeaningpeoplehavetakenthetimetoscaninorrekeydocumentation
fromthe1980swhichshippedwithvariousproducts.Iapplaudtheirefforts.Hopefullywewill
find some method of parsing through all of this documentation and updating it for today's
environment.Themostconfusingthingsyouwillreadarewhereactualproductliteraturesays,
Themaximumfilesizeis1,000,000,000bytesunlesslargedisksupportisenabled,thenyouare
limitedonlybythesizeofyourdisk. AtthepointintimewhentheauthorwrotethatthexBASE
formatcouldstore2GBandthelargestdiskdriveonthemarketwas1.08GB.Thestatementis
blatantly wrong now, but the online documentation is still trapped at that point in time. I
rememberthispointintimewell.Shortlyafterthatdocumentationcameout,SCSIdrivesstarted
increasinginsizeallofthewayupto8GBinlessthanayear.Alotofcustomershitthat2GB
wallprettyhard,thenreachedforlawyersclaimingfraud.Itwasn't
deliberatefraud,itwassimply
outdatedinformation.
MostonlinereferenceswillsaythatXbase(xBASE)isagenerictermforthedBASEfamily
ofdatabaselanguagescoinedinresponsetothreatsoflitigationoverthecopyrightedtrademark
dBASE . Thatwouldbetrueforapointintimelongago.TodayxBASEreallyreferstothe
data storage specification, not the language(s) involved in the application. People who are
programmersknowthis;peoplewhoaren't
programmersdon't
appeartohavetheabilitytoreason
itout.

Chapter1Fundamentals

19

IhavealreadytalkedaboutthevariousC/C++xBASElibrarieswhichareoutthere. Ifthe
definitionfoundonlineweretrue, itwouldrequirethoselibrariestoparseadBASEscriptand
executeit,ratherthandirectlyaccessthedataandindexfiles.Thesamewouldberequiredofthe
xBaseJlibrarywewillbecoveringinthisbook.Mostlibrariesdon't
provideanykindofscript
parsingcapability. Whattheydoprovidearefunctionswithnamesveryclosetosomeofthe
originaldBASEsyntax,alongwithalotofotherfunctionsthataccessthedataandindexfiles.
Puttingitinsimpleterms,xBASEisasystemofflatfileswhichcanorganizedatainauseful
mannerwhenoneormorespecificsetsofformatrulesarefollowed.Eachfileisintwoparts:a
file header and actual contents. Each header has twoparts: a file descriptor anda content
descriptor.Alotofdefinitionsyoufindpublishedandonlinewon't
usethewordcontent, they
willusethewordrecord. Thosedefinitionsareonlyaccurateforthedatafile.Whileitistrue
thateachindexvaluecouldbeviewedasarecordcontainingakeyvalue,recordnumber,sort
orderinformationandotherinternaldata,wedon't
haveanyconceptoftherecordorganization
unlesswearewritinganxBASElibraryofsomekind.
Theabovedoesnotdescribearelationaldatabasebyanystretchoftheimagination.There
have been various products on the market which put SQL type syntax around xBASE file
structures, but the organization really is flat file. If you have gone to school for computer
programming,youmayhaveencounteredthetermr elativefile. Arelativefileisaccessedby
record number, notakeyvalue. Itisoneofthesimplestfilestructurestocreate andisthe
foundationofseveralotherfilesystems.
You may have also encountered the term hashed file or hash file. This is an
enhancementtotherelativefile.Aparticularfieldorsetoffieldsischosenfromarecordtobe
consideredakey value. Someformofalgorithm(usuallyamathfunction)isfedthekeyvalue
and out the other side of the function comes the record number where the record you want
should be.Ifyouhaveareallybadhashalgorithmyouendupwithmultiplekeyshashingto
the same record number, a condition known as hash collision or simply collision . The
program then has to go sequentially through from that record either forward or backward
dependinguponkeysortorder,untilitfindstherecordyouarelookingfor,orakeysodifferent
thatitcantellyourrecordisn't
inthefile.Almosteveryprogrammerhastowriteaprogramlike
thiswhileearninghisorherbachelorsdegree.

20

Chapter1Fundamentals

TherewasalotofbrainpowerinvolvedwiththecreationofxBASE.Youmightremember
thatItoldyouitwasacreationwhichfelloutoftheJetPropulsionLaboratory andintothe
commercialworld.WhenyouwriteadatarecordtoanxBASEfile,itgetswrittencontiguously
inthenextavailableslot. Theactualrecordnumber isrecordedwiththekeyvalue(s)inthe
indexedfileswhicharebothopenandassociatedwiththedatafile. Whenyouwanttofinda
record,allofthedancingoccursinthefilecontainingtheindex.Asageneralrulekeyvaluesare
smallerthanrecordvaluessoyoucanload/traversemanymoreoftheminashorterperiodof
time.Oncetheenginelocatesthekey,ithastherecordnumberforadirectreadfromthedata
file. Thereallygoodlibrariesandengineswillalsoverifythekeyontherecordreadactually
matchesthekeyvaluefromtheindexfile.(Moreonthattopiclater.)
Idon't
knowwhatmagicactuallyhappenswhenthekeyisbeingprocessedandIdon't
care.
Ifyoureallywanttofindout,xBaseJcomeswithsourcecode,asdomanyotherOpenSource
projectswhichcreateandprocessxBASEfiles.Pulldownthesourceandplowthroughit.From
anapplicationdeveloperstandpoint, allweneedtoknowisthatiftheindexfileisopenand
associatedwiththedatafile,itwillbeupdated.Whenakeyisfoundwegettherecordandwhen
itisn'twegetanerrorvalue.
ItisimportanttonotethattheoriginalxBASEfilesystemsstoredonlycharacterdatainthe
datafiles.Numbersanddateswereallconvertedtotheircharacterrepresentations.Thissevere
restrictionmadethedesignhighlyportable. Binarydataisfarmoreefficientwhenitcomesto
storage,buttendstobearchitecturespecific.(RefertoThe MinimumYouNeedtoKnowtoBe
anOpenVMSApplicationDeveloper ISBN139780977086603page103foradiscussion
onLittleEndianvs.BigEndiananddataportability.)
Another benefit this severe restriction created was that it allowed nonprogrammers the
abilitytocreatedatabases.TheaverageJoehasnoideawhatthedifferencebetweenSingleand
Doubleprecisionfloatingpointisorevenwhateitherofthosephrasesmean.TheaverageMBA
wouldn't
knowwhatG_FLOAT,F_FLOAT,andD_FLOATwereorthattheyexistevenifthe
terms were carved on a 2x4 that smacked them between the eyes. The average user could
understand 9 digits in sizewith 3 decimal digits, though. By that time in America, most
everyonehadfilledoutsomegovernmenttaxwithholdingorotherformthatprovidedneatlittle
boxesforyoutowritedigitsin.

Chapter1Fundamentals

21

DOS,andbyextensionWindows,madesignificantuseofthreecharacterfileextensionsto
determinefiletypes. Linuxdoesn't
supportfileextensions. ItcanbeconfusingforaPCuser
when they see MYFILE.DBF on a Linux machine and they hear the . is simply another
characterinafilename.Itisevenmoreconfusingwhenyoureaddocumentationforapplications
writteninitiallyforLinux,likeOpenOffice,andittalksaboutfiles withanODT extension.I
camefrommultipleoperatingsystemswhichallusedfileextensions.Idon't
carethatI'm
writing
thisbookusingLotusSymphonyonKUbuntu,I'm
goingtocall.NNN afileextensionandthe
puristscanjustputtheirfingersintheirearsandhumreallyloud.
TheoriginalfileextensionforthedBASEdatafilewas.DBF.Somecloneplatformschanged
this,andsomedidnot.Itreallydependedonhowfaralongthelegalprocesswasbeforethesuits
weredropped.Intruth,youcouldusenearlyanyfileextensionwiththeprogramminglibraries
becauseyoupassedtheentirenameasastring.MostoftheC/C++,andJavalibrarieslookata
specialidentifiervalueinthedatafiletodetermineifthefileformatisdBASEIII,dBASEIV,
dBASEIIIwithMemo,dBASEIVwithMemo,dBASEVwithoutmemo,FoxProwithMemo,
dBASEIVwithSQLtable,Paradox,or oneoftheotherflavors. FoxbaseandFoxProwere
actuallytwodifferentproducts.
TheMemofieldwassomethingakintoatrainwreck.ThisaddedtheDBTfileextensionto
themix(FPTforFoxPro.)AMemofieldwasmuchasitsounded,alargefreeformtextfield.It
cameaboutlongbeforetheITindustryhadanagreeduponbest practice forhandlingvariable
lengthstringfieldsinrecords.ThefreeformtextgetsstoredasanentityintheDBTfile,anda
referencetothatentitywasstoredinafixedlengthfieldwiththedatarecord.
You have to remember that disk space was still considered expensive and definitely not
plentifulbackinthosedays.Oh,wethoughtwewouldneverfillupthat80MEGharddrivewhen
itwasfirstinstalled.Itdidn't
takelongbeforewewerebacktoarchivingthingswedidn't
need
rightawayonfloppies.
ThememofieldgavexBASEdevelopersamethodofadding commentssections torecords
withouthavingtoallocateagreatbigfieldineverydatarecord.Ofcourse,thememofieldhada
lotofdifferentflavors. Insomedialectsthememofieldinthedatarecordwas10bytesplus
howevermanybytesofthememoyouwantedtostoreinthedatarecord.ThedeclarationM25
would take 35 bytes in the record. According to the CodeBase++ version 5.0 manual from
SequiterSoftware,Inc.,thedefaultsizeforevaluatingamemoexpressionwas1024.Thebuiltin
memoeditor/wordprocessorfordBaseIIIwouldnotallowausertoeditmorethan4000bytesfor
amemofield.Youhadtoloadyourowneditortogetmorethanthatintoafield.

22

Chapter1Fundamentals

Memofilesintroducedtheconceptof blocksize tomanycomputerusersanddevelopers.


Whenamemofilewascreatedithadablocksizeassignedtoit.Allmemofieldswrittentothat
filewouldconsumeamultipleofthatblocksize.BlocksizesfordBASEIIIPLUSandClipper
memofileswerefixedat512andtherewasamaximumstoragesizeof32256bytes.Foxpro2.0
allowedamemoblocksizetobeanyvaluebetween33and16384.Everyblockhad8bytesof
overheadconsumedforsomekindofkey/indexvalue.
Areyouhavingfunwithmemofieldsyet? Theyconstituted agoodintentionwhichgot
forced intoallkindsofbastardizationsdue tolegalandOSissues. Sizelimitationsondisks
tendedtoexceedthesizelimitationsinmemory.DOSwasnotavirtualmemoryOS,andpeople
wantedANSIgraphics(color) applications,so,something hadtogive. Alotofapplications
startedsayingtheyweresettingthosemaximumexpressionsizestolimitmemofieldsto1024
bytes(1008iftheyknewwhattheyweredoing5128=504*2=1008.)Naturallytheusers
poppedrightpasttheendofthisastheyweretryingtowriteWarandPeaceinthenotesforthe
orderhistory. Sometimestheyweresimplytryingtoenterdeliveryinstructionsforruralareas
whenithappened.Therewerevariousstandard sizesofferedbyalloftheproductsduringthe
daysoflawsuitsandnastygrams.4096wasanotherpopularsizelimit,aswas1.5MEG.
Thelargermemosizelimitstendedtocomewhenwegotprotectedmoderuntimesthattook
advantage of the 80286 and 32bit DOS extenders which could take advantage of the
80386/80486architectures.(Theoriginal8086/8088CPUarchitecturecouldonlyaddress1Meg
ofRAMwhilethe80286couldaddress16Meginprotectedmode.The80386DXcouldaddress
4GB directly and 64TB of virtual memory.) I just checked the documentation at http://
www.dbase.com andtheyclaiminthecurrentproductthatamemofieldhasnolimit. Ialso
checkedtheCodeBase++5.0manual,andAppendixDstatesmemoentrysizeislimitedto64K.
The64KmagicnumbercamefromtheLIM(LotusIntelMicrosoft)EMS(ExpandedMemory
Standard). You can read a pretty good writeup in layman's terms by visiting http://
www.atarimagazines.com/compute/issue136/68_The_incredible_expan.php
Ifyouthinkmemofieldswerefun,youshouldconsidertheindexedfilesthemselves.Indexes
aren't
storedwiththedatainxBASEformats.OriginallyeachindexwasoffinitsownNDXfile.
Youcouldopenadatafilewithoutopeninganyassociatedindex,write(ordelete)recordsfromit,
then close, without ever getting any kind of error. As a general rule, most p roduction
applicationswhichusedxBASEfileswouldopenthedatafile,thenrebuildtheindextheywanted,
sometimesusingauniquefilename. ThispracticeendedupleavingalotofNDXfileslaying
aroundondiskdrives,butmostdevelopersengaginginthispracticeweren't
trainedprofessionals,
theyweresimplygettingpaidtoprogram;there isadifference.

Chapter1Fundamentals

23

Itdidn't
takelongbeforewehadMultipleIndexFiles(MDX),CompoundIndexFiles(CDX),
ClipperIndexFiles(NTX),DatabaseContainer(DBC),andfinallyIDXfiles,whichcouldbe
eithercompressedoruncompressed.TheremayevenhavebeenothersIdon'tremember.
MDX was a creation which came with dBASE IV. This was a direct response to the
problemsencounteredwhenNDXfilesweren't
updatedasnewrecordswereadded.Youcould
associatea production MDXfilewithaDBFfile.Itwaspromisedthattheproduction MDX
file would be automatically opened when the database was opened...unless that process was
deliberately overridden by a programmer. This let the runtime keep indexes up to date.
AdditionalkeyscouldbeaddedtothisMDXuptosomemaximumsupportednumber.Ishould
point out that a programmer could create nonproduction MDX files which weren'topened
automaticallywiththeDBFfile. (xBaseJiscurrentlyknowntohavecompatibilityissueswith
dBASEVformatsandMDXfilesusingnumericand/ordatekeydatatypes.) MDXcalledthe
keysitstoredtagsandallowedupto47tagstobestoredinasingleMDX.
WhilethereissomecommonalityofdatatypeswithxBASEfilesystems,eachcommercial
versiontriedtodifferentiateitselffromthepackbyprovidingadditionalcapabilitiestofields.
Thisresultedinalotofcompatibilityissues.

Type

Description

AutoincrementSameaslong

Timestamp8bytestwolongs,firstfordate,secondfortime. Thedateisthe
number of days since 01/01/4713 BC. Time is hours * 3600000L + minutes *
60000L+Seconds*1000L

10digitsrepresentinga.DBTblocknumber.Thenumberisstoredasastring,right
justifiedandpaddedwithblanks.AddedwithdBaseIV.

ASCIIcharactertextoriginally<254charactersinlength.ClipperandFoxProare
knowntohaveallowedthesefieldstobe32Kinsize.Onlyfields<=100characters
canbeusedinanindex.Someformatschoosetoreadthelengthasunsignedwhich
allowsthemtostoreupto64Kinthisfield.

DatecharactersintheformatYYYYMMDD

FloatingpointsupportedbydBASEIV,FoxPro,andClipper,whichprovidesupto
20significantdigitsofprecision.Storedasrightjustifiedstringpaddedwithblanks.

OLE10digits(bytes)representinga.DBTblocknumber,storedasstring,right
justifiedandpaddedwithblanks.CameaboutwithdBASEV.

Chapter1Fundamentals

24

Type

Description

Long4bytelittleendianinteger(FoxPro)

LogicalBoolean8bitbyte.Legalvalues
?=Notinitialized
Y,yYes
N,nNo
F,fFalse
T,tTrue
Values are always displayed as T, F, or ?. Some odd dialects (or more
accuratelyC/C++librarieswithbugs)wouldputaspaceinanuninitializedBoolean
field.Ifyouareexchangingdatawithothersources,expecttohandlethatsituation.

10digits(bytes)representingaDBTblocknumber.Storedasrightjustifiedstring
paddedwithspaces.
SomexBASEdialectswouldalsoallowdeclarationasMnn,storingthefirstnnbytes
ofthememofieldintheactualdatarecord.Thisformatworkedwellforsituations
wherearecordwouldgeta1015characterSTATUScodealongwithafreeform
descriptionofwhyithadthatstatus.
Paradoxdefinedthisasavariablelengthalphafieldupto256MBinsize.
UnderdBASEtheactualmemoentry(storedinaDBTfile)couldcontainbinary
data.

xbaseJdoesnotsupporttheformatMnnandneitherdomostOpenSourcetools.
N

NumericField19characterslong.FoxProandClipperallowthesefieldstobe20
characters long. Minus sign, commas, and the decimal point are all counted as
characters. Maximum precision is 15.9. The largest integer value storable is
999,999,999,999,999.Thelargestdollarvaluestorableis9,999,999,999,999.99

Doublenoconversions,storedasdouble

Picture(FoxPro)Muchlikeamemofield,butforimages

Paradox3.5andlater.Fieldtypewhichcouldstore16bitintegers.

Chapter1Fundamentals
Type

25

Description

DateTime(FoxPro)

Currency(FoxPro)

Therewasalsoabizarrecharacternamevariablewhichcouldbeupto254characterson
someplatforms,but64KunderFoxbaseandClipper.Idon't
haveacodeforit,andIdon't
care
aboutit.
Limits,Restrictions,andGotchas
OurlibraryofchoicesupportsonlyL,F,C,N,D,P,andMwithoutanynumbersfollowing.
Unlessyouforcecreationofadifferentfiletype,thislibrarydefaultstothedBASEIIIfileformat.
YoushouldnevereveruseadBASEIIfileformator,moreimportantly,adBASEIIproduct/tool
onadatafile.Thereisafieldonthefileheaderwhichcontainsadateoflastupdate/modification.
dBASEIIIandlaterproductshavenoproblems,butdBASEIIceasedworkingsometimearound
Jan1,2001.
Mostoftoday's
librariesandtoolssupportdBASEIIIfiles.Thismeanstheysupportthese
fieldandrecordlimitations:

dBASEIIallowedupto1000bytestobeineachrecord.dBASEIIIallowedupto4000bytes
ineachrecord.Clipper5.0allowedfor8192bytesperrecord.LaterdBASEversionsallowed
upto32767bytesperrecord.Paradoxallowed10800forindexedtablesbut32750fornon
indexedtables.

dBASEIIIallowedupto1,000,000,000bytesinafilewithoutlarge disksupport enabled.


dBASEIIallowedonly65,535records.dBASEIVandlaterversionsallowedfilestobe2GB
insize,butalsohada2billionrecordcap.AtonepointFoxProhada1,000,000,000record
limitalongwitha2GBfilesizelimit.(Dothemathandfigureoutjusthowbigtherecords
couldbe.)

dBASEIIIallowedupto128fieldsperrecord.dBASEIVincreasedthatto255.dBASEII
allowedonly32fieldsperrecord.Clipper5.0allowed1023fieldsperrecord.

dBASEIVhadamaximumkeysizeof102bytes. FoxProallowedupto240bytesand
Clipper388bytes.

Field/columnnamescontainamaximumof10characters.

26

Chapter1Fundamentals

IlistedsomeofthenondBASEIIIvaluestogiveyouasenseofwhatyoumightbeup
againstwhenafriendcallsyouupandsaysI 'v
e gotsomedataonanoldxBASEfile,canyou
extractitforme? TheflavorsofxBASEwhichwentwellbeyondevendBASEIVlimitations
haveverylimitedsupportintheOpenSourcecommunity.
Letmesaythisplainlyforthosewhohaven't
figureditout:xbaseislikeLinux.Therearea
zilliondifferentflavors,notwoofwhicharethesame,yet,afewcorethingsarecommon,sothey
arealllumpedtogetherunderoneheading.
Ifyoureadthroughthecommentsinthesourcefiles,you'll
seethatxBaseJclaimstosupport
onlydBASEIIIanddBASEIV.Ifyouarelookingfortransportabilitybetweenmanysystems,
thisistheleastcommondenominator(LCD)andshouldworkinmostcases.Thecommentsmay
verywellbeoutofdate,though,becausethecreateDBF()protectedmethodoftheDBFclass
supportsaformatvaluecalledFOXPRO_WITH_MEMO.
When I did a lot of C/C++ programming on the PC platform, I found GDB (Greenleaf
DatabaseLibrary)tobethemostrobustlibraryavailable. IhadusedCodeBasefromSequiter
Softwareandfoundittobedramaticallylacking.WiththeCversionoftheirlibrary,youcould
not develop an application which handled dBASE, FoxPro, and Clipper filessimultaneously.
Theirentireobjectlibrarywascompiledfor asingleformatatatime. GDBcreatedseparate
classesandseparatefunctionstohandleopening/creatingallofthedatabaseformatsitsupported.
Eachofthoserootclasses/structuresweretaskedwithkeepingtrackofandenforcingthevarious
limitseachfiletypeimposed.ThelibrarywasalsotestedunderWindows,Win32,genericDOS,
16bitDOS,32bitDOS,andOS/2.Itwasthecreamofthecropandverywellmaystillbetoday.
I'm
bringingupthosecommerciallibrariestomakeapointhere.Afterreadingthroughthe
code,IhavecometotheconclusionthatonlytheformatwasimplementedbyxBaseJ,notallof
therules.WhenyoureadthesourcefortheDBFclass,youwillseethatifweareusingadBASE
IIIformat,afieldcountof128isenforced,andeverythingelseislimitedto255.Thetruthisthat
theoriginalDOSbasedFoxbasehadafieldlimitof128aswell,butthatformatisn't
directly
supported.
Thereisalsonocheckformaximumrecordlength. TheDBFclasshasaprotectedshort
variablenamedlreclwhereitkeepstrackoftherecordlength,buttherearenoteststhatIcould
seeimplementingthevariousmaximumrecordlengths.Intruth,sinceitsupportsonlyasubsetof
theformats,ahardcodedtestcheckingagainst4000wouldworkwellenough.NotalotofDOS
usersouttherewithlegitimatedBASEIIIPlusruntimestoworryabout.

Chapter1Fundamentals

27

Anothergotchatowatchoutforismaximumrecords.TheDBFclasscontainsthislineof
code:
file.writeInt(Util.x86(count));

AlltheUtil.x86calldoesisreturna4bytebuffercontainingabinaryrepresentationofalong
intheformatusedbyanx86CPU.(Javahasitsowninternalrepresentationforbinarydatawhich
mayormaynotmatchthecurrentCPUrepresentation.)Thevariablefile issimplyaninstance
of the Java RandomAccessFile class, and writeInt() is a method of that class. There is no
surrounding check to ensure we haven'texceeded a maximum record count for one of the
architectures.OurvariablecounthappenstobeaJavaintwhichis32bits.WeknowfromourC
programmingdays(oratleasttheCheaderfilelimits.h)thefollowingthings:

Typ
ypee

16bit

32bit

unsigned

65,535

4,294,967,295

signed

32,767

2,147,483,647

WhilewewillnothavemuchtroublewhenhandingdataovertotheotherOpenSourcetools
whichdon't
checkmaximums,wecouldhavetroubleifweaddedalotofrecordstoafileflagged
asdBASEIIIthenhandeditofftoanactualdBASEIIIruntime.Recordmaximumsweren't
as
bigaproblemasfilesize.Thatfunky1billionbytefilesizelimitwasaresultofDOSandthe
drivetechnologyoftheday.Wehada1Gigwallforawhile.Evenafterthatbarrierhadbeen
pushedbackto8Gigwestillhadthatbuiltin1Giglimitdueinlargepartto16bitmathandthe
FAT16diskstructureusedatthetime.MostofyounowusediskstorageformatslikeFAT32,
NTFS,HPFS,EXT3,orEXT4.Noneofthesenewerformatshavethe16bitproblemswehadin
daysgoneby.(Forwhatitisworth,DOSfloppyformatstillusesFAT16.)
1diskblock=512bytes
1K=1024bytesor2blocks
1Meg=1Ksquaredor10242blockunits
1GB=1Kcubedor1024bytes*1024*1024=1,073,741,824
1GB/512=2,097,152diskblocks
2GB=2*1GB=2,147,483,648(notice1greaterthanmaxsigned32bitvalue)
2GB/512=4,194,304diskblocks
4GB=4*1GB=4,294,967,296(notice1greaterthanmaxunsigned32bitvalue)
4GB/512=8,388,608diskblocks
32767*512=16,776,704
16Meg=16*1024*1024=16,777,216

28

Chapter1Fundamentals

Largedisksupport,sometimesreferredtoaslarge filesupport gotitsnamefromtheDOS


FDISKcommand. WheneveryoutriedtousetheFDISKcommand afterWindows95OSR2
cameout onadisklargerthan512MB,itwould askyouifyouwantedtoenablelargedisk
support.WhatthatreallydidwasswitchfromFAT16toFAT32.UnderFAT32youcouldhave
fileswhichwereupto4GBinsizeandapartition2TBinsize.Iprovidedthecalculationsabove
soyouwouldhavesomeideaastowherethevariouslimitscamefrom.
Today xBASEhasa2Gigfilesizelimit. AslongasxBASEremains32bitanddoesn't
calculatethesizewithanunsignedlong,thatlimitwillstand.ItoldyoubeforethatxBASEisa
relativefileformatwithrecordscontiguously placed. Whenyouwanttoloadrecord33,the
libraryorxBASEenginetakesthestartofdataoffsetvaluefromthefileheader,thenaddstoit
therecordnumberminusonetimestherecordsizetoobtaintheoffsetwhereyourrecordstarts.
Recordnumbersstartatone,notzero. SomeC/C++librariesusetheexactsamemethodfor
writingchangestothedatafileastheydoforwritingnewrecords.Iftherecordnumberprovided
iszero,theywriteanewrecord;otherwisetheyreplaceanexistingrecord.
Incasethepreviousparagraphdidn't
makeitobvioustoyou,datarecordsarefixedlength.
Donotconfuseentriesinamemofilewithdatarecords.Youcan't
createanindexonamemo
file,orreallydomuchmorethanreadorwritetoit.
Variousfileandrecordlockingschemashavebeenusedthroughouttheyearsbythevarious
xBASE flavors. During the dark days of DOS, a thing called SHARE.EXE came with the
operatingsystem.Itneverworkedright.
SHAREcouldlockchunksoffiles. ThisledtoproductslikeMSAccessclaimingtobe
multiuserwhentheyweren't.
ItalsoleadtotheinfamousTwo UserBoof bug.Access(and
severalotherdatabaseproductsatthetime)decidedtoorganizetheinternaldatabasestructure
around arbitrary pagesizes. Apagewasbasicallysomenumberof512byteblocks. Itwas
commontoseepagesizesof8196bytes,whichwas16blocks.SHAREwouldthenbeinstructed
to lock a page of the database file. A page actually contained many records. If two users
attemptedtomodifydifferentrecordsonthesamepage,theseconduser's
updatewoulddutifully
beblockeduntilthefirstuser's
updatewaswrittentodisk.IOwasperformedapageatatimein
ordertoincreaseoverallefficiency.Theupdatelogicwoulddutifullycheckthecontentsofthe
modifiedrecordondisktoensurenobodyelsehadchangeditbeforeapplyingtheupdates.What
theIOprocessdidn't
dowascheckeverydamnedrecordinthepageforchanges.Thelastonein
won.Allchangesmadebythefirstuserwerelost.Somedevelopersendedupmakingarecord
equaltoapageasacheaphacktypeworkaround.Alotofdiskwaswastedwhenthiswasdone.

Chapter1Fundamentals

29

Summary
Despiteallofitslimitationsandfaults,thexBASEdatastoragemethodwasgroundbreaking
whenithitthemarket.Withoutsomeformofindexedfilesystem,thePCwouldnothavecaught
on.
Itisimportantforbothusersanddevelopersto understand thelimitationsofany chosen
storage method before developing an application or systems around that method. While a
relationaldatabaseismuchmorerobustfromadatastoragestandpoint,itrequiresalotmore
investment and overhead. Even a free relational database requires someone to install and
configureitbeforeanapplicationcanbewrittenusingit.AdevelopercanuseaC/C++/Java/etc.
library and create a single executable file which requires no configuration, simply an empty
directorytoplaceitin.Thatprogramcancreateallofthefilesitneedsthenallowausertostore
andaccessdatainameaningfulfashionwithoutthemhavinganysignificantcomputerskills.
There willalways be arole for standalone indexed filesystems. Both commercial and
OpenSourcevendorsneeddatastoragemethodswhichrequirenousercomputerskills.Justhow
manycopiesofQuickendoyouthinkwouldhaveeversoldifauserhadtodownload+install
+configureaMySQLdatabasebeforeQuickenwouldinstallandletthemtracktheirexpenses?
Nomatterhowoldthetechnologyis,theneedforitstillexists.
ReviewQuestions
1. HowmanyfieldsdiddBASEIIIallowtobeinarecord?
2. WhatgeneralcomputingtermdefinesthetypeoffileanxBASEDBFreallyis?
3. WhatdoesxBASEmeantoday?
4. WhatwasthenoncommercialpredecessortoallxBASEproducts?
5. IntermsofthePCandDOS,wheredidthe64Kobject/variablesizelimitreallycomefrom?
6. WhatcompanysoldthefirstcommercialxBASEproduct?
7. IsthereanANSIxBASEstandard?Why?
8. WhatisthemaximumfilesizeforaDBFfile?Why?
9. WhatwasthemaximumnumberofbytesdBASEIIIallowedinarecord?dBASEII?
10. Whatform/typeofdatawasstoredintheoriginalxBASEDBFfile?
11. CanyoustorevariablelengthrecordsinaDBFfile?
12. DoesanxBASElibraryautomaticallyupdateallNDXfiles?
13. WhatistheacceptedmaximumprecisionforaNumericfield?
14. Whatisthemaximumlengthofafieldorcolumnname?

Chapter1Fundamentals

30

Pageleftblankintentionally.

Chapter1
Chapter1
Fundamentals
1.1 Ou
nt
OurrEnvironme
Environmen
IamwritingthebulkofthiscodeonadesktopPCrunningthe32bitKarmicKoalapre
releaseofKUbuntu.IhaveSunJava6installedonthismachine,butseveralearlierreleasesof
Javashouldworkjustfinewiththislibrary.
After unzipping the download file, I copied the JAR files into a working directory. Of
course,thenewerJavaenvironmentswillonlylookforclassfileslocally,notJARfiles,soyou
needtocreateaCLASSPATHenvironmentvariable.Iusethefollowingcommandfilesinceit
loadsjustabouteverythingIcouldwantintoCLASSPATH:
env1
1)
2)
3)
4)
5)
6)
7)
8)
9)
10)
11)
12)
13)
14)
15)
16)
17)
18)
19)
20)
21)
22)
23)
24)
25)

#! /bin/bash
#set -v
#sudo update-java-alternatives -s java-6-sun
export JAVA_HOME='/usr/lib/jvm/java-6-sun'
set_cp() {
local curr_dir=$(echo *.jar | sed 's/ /:/g')':'
local jvm_home_jars=$(echo $JAVA_HOME/*.jar | sed 's/ /:/g')':'
local shr_jars=$(echo /usr/share/java/*.jar | sed 's/ /:/g')':'
local loc_jars=$(echo /usr/local/share/java/*.jar | sed 's/ /:/g')':'
if [ "$curr_dir" == "*.jar" ]; then
unset curr_dir
fi;
export CLASSPATH=$(echo .:$curr_dir$jvm_home_jars$shr_jars$loc_jars)
}
ecp() {
echo $CLASSPATH | sed 's/:/\n/g'
}
# set class path by default
set_cp
#set +v

roland@logikaldesktop:~$ cd fuelsurcharge2
roland@logikaldesktop:~/fuelsurcharge2$ echo $CLASSPATH
roland@logikaldesktop:~/fuelsurcharge2$ source ./env1
roland@logikaldesktop:~/fuelsurcharge2$ echo $CLASSPATH
.:commons-logging-1.1.1.jar:junit.jar:xBaseJ.jar:xercesImpl.jar:/usr/lib/jvm/
java-6-sun/*.jar:/usr/share/java/hsqldb-1.8.0.10.jar:/usr/share/java/hsqldb.jar:/
usr/share/java/hsqldbutil-1.8.0.10.jar:/usr/share/java/hsqldbutil.jar:/usr/share/
java/ItzamJava-2.1.1.jar:/usr/share/java/jsp-api-2.0.jar:/usr/share/java/jspapi.jar:/usr/share/java/LatestVersion.jar:/usr/share/java/libintl.jar:/usr/share/
java/mysql-5.1.6.jar:/usr/share/java/mysql-connector-java-5.1.6.jar:/usr/share/
java/mysql-connector-java.jar:/usr/share/java/mysql.jar:/usr/share/java/
QuickNotepad.jar:/usr/share/java/servlet-api-2.4.jar:/usr/share/java/servletapi.jar:/usr/local/share/java/*.jar:

32

Chapter1Fundamentals

Asyoucansee,thatscriptfindseveryJARfileandaddsittomyenvironmentvariable.The
occasional*.jar valueinthesymboldefinitiondoesn't
appeartoimpacttheJVMwhenitgoes
searchingforclasses. Ifyoudon't
havetheJARfilesspecificallylistedinyourCLASSPATH
variable,thenyouwillseesomethinglikethisthefirsttimeyoutrytocompile:
roland@logikaldesktop:~/fuelsurcharge2$ javac example1.java
example1.java:3: package org.xBaseJ does not exist
import org.xBaseJ.*;
^
example1.java:4: package org.xBaseJ.fields does not exist
import org.xBaseJ.fields.*;
^
example1.java:5: package org.xBaseJ.Util does not exist
import org.xBaseJ.Util.*;
^
example1.java:18: cannot find symbol
symbol : variable Util
location: class example1
Util.setxBaseJProperty("fieldFilledWithSpaces","true");

Windows users will need to view the information provided by Sun on how to set the
CLASSPATHvariable.
http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/classpath.html
Youmayalsowishtolookatthismessagethread:
http://www.computing.net/answers/programming/howtosetjavaclasspathinvista/15739.html
1.2 Op
OpeenorCrea
norCreatte?
YouwillfindquiteafewexamplesofvariousxBASEprogramminglanguages/toolsonthe
Web. ExamplesarealittlescarceforxBaseJ,asisdocumentation,hence,thecreationofthis
book.Mostoftheexamplespissmeoff.Iunderstandthattheyaretryingtoshowthesimplestof
thingstoauserwhomayhavenoothercomputerknowledge,butthosewellmeaningexamples
showauserhowtodothingsbadly,andthatisexactlyhowtheywillcontinuetodothem.
ThemainWebsiteforxBaseJhastwoexampleswhich,whilewellmeaning,fallintothis
category: example1.javaandexample2.java. Thefirstcreatesadatabase,thesecondopensit.
Whileyoucanarguethatthecreatealwayswantedtocreate,justhavingtheopenexamplecrash
outwhenthefileismissingisprobablynotwhatyouwantwhendevelopinganapplicationwhich
willbesentoutintotheuniverse.Mostofyoudon't
eventhinkaboutwhysomeapplicationstake
solongtostartuptheveryfirsttimeyourunthem.Thestartupcodeforthoseapplicationsisvery
graciouslyrunningaroundcheckingforallofthenecessarydatafiles.Whenfilesaremissing,it
createsdefaultones.Justhowmanyofyouwoulduseawordprocessorifitrequiredyoutorun
somespecial(probablyundocumented)programbeforeitwoulddoanythingotherthancrashout?

Chapter1Fundamentals

33

WhenIimbibeenoughcaffeineandthinkaboutit,therealproblemistheconstructorusinga
Booleanforthedestroy parameter.ABooleangivesyouonlyTrueandFalse.Aproduction
classsystemneedsthreeoptions:
1. Useexisting
2. Overwrite
3. Createifmissing
Ifyouhavereadsomeofmyotherbooksyouwillknowthatmanylanguagesnamethistype
ofparameterorattributedisposition orfile disposition. TheDBFconstructordoesn't
havea
filedispositionattribute,sowehavesomelessthangreatexamplesfloatingaround.
I'm
notgoingtodiscussJavamuchinthisbook.IwillpointoutodditiesasIseethem,butif
youarelookingforaJavatutorial,therearemanyofthoseontheWeb.I've
evenwrittenabook
onJavawhichsomepeoplelike.(The MinimumYouNeedtoKnowAboutJavaonOpenVMS
Volume1 ISBN139780977086610) I'm
aveteransoftwaredeveloper,butnotatenured
Javadeveloper.Afewdiscussionsofodditiesaside,wearereallyfocusingonhowtousexBaseJ
withJavainthisbook.
Thereareveryfewclassesofapplicationswhichalwaysneedtocreateanindexedfilewhen
theyrun.MostbusinesssystemsusethedispositionofCreate ifmissing. Manywilldisplay
some kind of messagestating they arecreating a missing indexedfile, just in case it wasn't
supposedtobemissing,butingeneral,onlyextracttypeapplicationsalwaysneedtocreatewhen
itcomestoindexedfiles.
Incaseyoudonotunderstandthephraseextracttype applications, these areapplications
which arerunagainstlargedatasetsthatpulloutcopiesofrecords/rowswhichmeetcertain
criteriaandplacethesecopiesinafile.Thefileisknownasanextractfileandtheapplication
whichcreatesitanextractapplication.

Chapter1Fundamentals

34
1.3 Example1

example1.javaisrepresentativeofthefirstexampleprogramfloatingaroundontheWebat
thetimeofthiswriting.Notethatsomeolderexamplesdon't
showtheproperimportstatements.
YouneedtoincludethefullpathasIhavedonewithlistinglines3through5.
example1.java
1)
2)
3)
4)
5)
6)
7)
8)
9)
10)
11)
12)
13)
14)
15)
16)
17)
18)
19)
20)
21)
22)
23)
24)
25)
26)
27)
28)
29)
30)
31)
32)
33)
34)
35)
36)
37)
38)
39)
40)
41)
42)

import
import
import
import
import

java.io.*;
java.util.*;
org.xBaseJ.*;
org.xBaseJ.fields.*;
org.xBaseJ.Util.*;

public class example1 {


public static void main(String args[]){
try{
//
// You must set this unless you want NULL bytes padding out
// character fields.
//
Util.setxBaseJProperty("fieldFilledWithSpaces","true");
//Create a new dbf file
DBF aDB=new DBF("class.dbf",true);
//Create the fields
CharField classId = new CharField("classId",9);
CharField className = new CharField("className",25);
CharField teacherId = new CharField("teacherId",9);
CharField daysMeet = new CharField("daysMeet",7);
CharField timeMeet =new CharField("timeMeet",4);
NumField credits = new NumField("credits",2, 0);
LogicalField UnderGrad = new LogicalField("UnderGrad");
//Add field definitions to database
aDB.addField(classId);
aDB.addField(className);
aDB.addField(teacherId);
aDB.addField(daysMeet);
aDB.addField(timeMeet);
aDB.addField(credits);
aDB.addField(UnderGrad);

aDB.createIndex("classId.ndx","classId",true,true);
// true delete ndx, true - unique index,
43)
aDB.createIndex("TchrClass.ndx","teacherID+classId", true, false);
//true - delete NDX, false - unique index,
44)
System.out.println("index created");
45)
46)
classId.put("JAVA10100");
47)
className.put("Introduction to JAVA");
48)
teacherId.put("120120120");
49)
daysMeet.put("NYNYNYN");
50)
timeMeet.put("0800");
51)
credits.put(3);

Chapter1Fundamentals

35

52)
UnderGrad.put(true);
53)
54)
aDB.write();
55)
56)
classId.put("JAVA10200");
57)
className.put("Intermediate JAVA");
58)
teacherId.put("300020000");
59)
daysMeet.put("NYNYNYN");
60)
timeMeet.put("0930");
61)
credits.put(3);
62)
UnderGrad.put(true);
63)
64)
aDB.write();
65)
66)
classId.put("JAVA501");
67)
className.put("JAVA And Abstract Algebra");
68)
teacherId.put("120120120");
69)
daysMeet.put("NNYNYNN");
70)
timeMeet.put("0930");
71)
credits.put(6);
72)
UnderGrad.put(false);
73)
74)
aDB.write();
75)
76)
77)
}catch(Exception e){
78)
e.printStackTrace();
79)
}
80) }
81) }
roland@logikaldesktop:~/fuelsurcharge2$ javac example1.java
roland@logikaldesktop:~/fuelsurcharge2$ java example1
index created
roland@logikaldesktop:~/fuelsurcharge2$

Listingline18containsaveryimportantpropertysetting. Bydefault,xBaseJpadsstring
fieldswithNULLbyteswhenwritingtodisk.Whiletherewasatimewhenthiswasdone,most
xBASEenvironmentsdidawaywiththatpractice.Asmoreandmoretoolsbecameabletoopen
rawdatafiles,itbecamenecessarytosupplyspaces.Pleaseconductthefollowingtest:
1.
2.
3.
4.

CompileandrunthisprogramasIhavedone.
UseOpenOfficetoopenclass.dbfinaspreadsheet.Lookcloselyatthedata.
Commentoutlistingline18;compileandrerunthisprogram.
UseOpenOfficetoopenclass.dbfinaspreadsheet.Lookcloselyatthedata.

Whatyouwillnoticeisthatthefirstspreadsheethadsomefunkylookingzerocharactersin
thetextcolumns. Thosecharacterswerethenullbytespaddingoutthecharacterfields. The
secondversionofthefileopenedmoreasyouexpected.Itshouldlookmuchlikethefollowing:

36

Chapter1Fundamentals

PleasenotecolumnFonthespreadsheet. Eventhoughthenumericdatabasecolumnwas
declared tohavetwodigits,wedon'tgetaleadingzero. ColumnE(TIME)mayseemabit
deceiving at first. This wasn'tdeclared as a numeric database column; it was declared as a
charactersotheleadingzerocouldbeforced.Listingline29iswhereCREDITS(columnF)is
declared,andlistingline28declaresTIMEMEET(columnE). Pleasenotethatnumericfield
declarations have two numeric parameters. The first is the size of the field including the
punctuationcharacters,andthesecondisthenumberofdecimalplaces.
Listingline21iswheretheinitialemptydatabasefileiscreated.TheBooleanvaluetrueas
thefinalparameterforcesfilecreation.
Onceyoucreateafield,ithastobeaddedtothedatabasebeforeitbecomesacolumninthe
database.Wedothisatlistinglines34through40.
Anindexeddatafileisn'tmuchuseunlessithasatleastoneindex. Twoindexfilesare
createdatlistinglines42and43. ThefirstBooleanvaluepassedintothesemethodscontrols
deletionofexistingfiles.Thesecondvaluecontrolswhethertheindexisauniquekeyornot.A
uniquekeywillallowonlyoneinstanceofavaluetobestoredasakeyforonlyonerecord.A
nonuniquekeywillallowthesamekeyvaluetopointtomultiplerecords.Youcannotguarantee
whatorderrecordswillberetrievedforrecordshavingthesamekeyvalue.Ifsomeonerebuilds
theindexfile,oraddsotherrecordsinthatrange,orpacksthedatabase,theretrievalordercan
change.JustbecausetherecordforFRED SMITH cameoutfirstinthisdepartmentreportrun
doesn'tmeanitwillcomeoutfirstnexttime.

Chapter1Fundamentals

37

Note:xBASEfilesdonotphysicallydeleterecords.Theyflagrecordsasbeingdeleted.The
onlywaytoreclaimthewastedspaceistocreateanewversionofthedatabasefilewithafunction
knownasPACK.Oneoftwothingswouldhappendependinguponthetoolinvolved:
1. Thedatafilewouldbewalkedthroughsequentiallyandrecordsnotflaggedasdeletedwould
beshuffledup,replacingdeletedornewlyemptiedrecords.
2. Anewversionofthedatabasewouldbecreatedwithatemporaryname.Thisversionwould
containonlythenondeletedrecordsfromtheoriginaldatabase.Uponcompletiontheoriginal
databasewouldbedeletedandthenewonerenamed.
Thesecondapproachwasmuchmoreefficient,butrequiredalotofdiskspace.Nomatter
whichapproachwastaken,allindexfilesforthedatabasehadtoberebuilt.UntilwehadMDX
files,mostlibrariesanddialectsofxBASEhadanoptionwhichwouldallowadevelopertocreate
anindexaneweachtimetheyopenedthefile.xbaseJhasthesameoption:
public

NDX(String name,
String NDXString,
DBF indatabase,
boolean destroy,
boolean unique) throws

xBaseJException, IOException

Whenyoupassinadestroyflagoftrue,xBaseJrebuildstheindexbasedupontherecords
currentlyinthedatabase.PleasenotethatifyoudonotPACKthedatabasepriortocreatinga
newindex,thenewindexwillcontainentriesfordeletedrecords.WhenyouopenanMDXtag,
theindexisautomaticallyrebuiltinplace.Wewilldiscusspackingmorelater.
PleaseturnbacktothescreenshotshowingthisdatabaseinanOpenOfficespreadsheetand
reallylookatrowone.Whenthisexampleprogramwaswrittentheprogrammerusedmixedcase
column names because that looked very Javalike. (It actually looked very PASCALian, and
PASCALisadeadlanguage,soyoudothemathonthatone.)Noticewhatactuallygotwrittento
thedatabase,though:itisalluppercase.Ihavefoundbugsovertheyearsinvarioustoolswhich
enduplettinglowercaseslipintotheheaderrecord.Itisagoodprogrammingpracticetoalways
useuppercaseinsideofstringswhichwillbeusedforcolumnorindexnames.Youwillneverbe
burnedbyanupcase()bugifyoualwayspassinuppercase.
Takeareallygoodlookatlistingline43. Thatkeyiscomposedoftwodatabasecolumns
concatenatedtogether.Onpage17ofthisbookyouweretoldthattheoriginaldBASEversion
supportedonlycharacterdata.Alln umeric valueswerestoredintheircharacterrepresentations
toincreaseportability.Thisfeaturealsomadeindexcreationwork.Wearen't
addingtwovalues
togetherwiththatsyntax,weareconcatenatingtwostringsintoonesort/keyvalue.

38

Chapter1Fundamentals

Intruth,that false parameterattheendoflistingline43isoflittlelogicalvalue.Yes,the


databasewillsetthekeyuptoallowforduplicatevalues,buttheycannothappen.Listingline42
hasdeclaredauniquekeybaseduponthecolumnCLASSID. Ifoneportionofastringkeyis
requiredtobeuniqueduetosomeotherconstraint,thenallvaluesinthatkeywillbeunique.
Listinglines46through54demonstratehowtoassignvaluestothefieldsandfinallywritea
shinynewrecordtothedatabase.Becausewehavetheindexfilesopenandassociatedwiththe
datafile,alloftheirkeyswillbeupdated.Imustconfesstobeingabitdisappointedatthechoice
ofput()asthemethodnameforassigningfieldvalues.Iwouldhaveexpectedassign()orset().
Dependinguponwhenthismethodwaswritten,though,put()mighthavebeeninvogue.There
wasadarkperiodoftimeintheworldofJavawhenanobjectitselfwasconsideredadatastore,
andwhenitwasthoughtthatoneshouldalwaysput intoadatastore.TheJavaprogrammers
reallyjustwantedtodosomethingdifferentthantheC++developerswhowereusingsetMyField
(),assignMyField(),etc.Ofcourse,GDBusedtohaveafunctionDBPutNamedStr()whichwrote
astringvalueintotheIObufferforanamedfield,somaybeinhindsightI'mjustpicky.
That's
it.Thefirstexampletoforcecreationofadatafilealongwithtwoindexfiles.Three
recordsarewrittentothefile,andwehaveverifiedthisbyopeningthefilewithOpenOffice.One
thingIdon't
likeaboutthisexampleisthatitdidn't
bothertousetheclose()methodoftheDBF
object. While it is true that close() is called by the finalize() method which is called upon
destruction,itisalwaysagoodprogrammingpracticetocloseyourfilesbeforeexiting.

Chapter1Fundamentals

39

1.4 Exce
ption
ngandExample1
Excep
tionHandli
ndlin
IwillassumeyouarefamiliarenoughwithJavatoknowthatstatementswhichcanthrowan
exceptiontraditionallygetencapsulatedinatry/catchblock. Nearlyeveryexceptionclassyou
willeverencounterisderivedfromtherootJavaExceptionclass.Thisallowseverycatchblock
seriestohaveanultimatecatchallliketheoneyouseeatlistingline77.Asfaraserrorhandling
goes,itdoesn't
dosquatfortheuser.Thereisnorecoveryandtheywillhavenoideawhatthe
stacktracemeans.
Thecodeinthetryblockisreallywherethetrainwentofftherails.Yes,Iunderstandthe
intentwastoshowonlythemoststraightforwardmethodofcreatinganewxBASEfilewithan
index.Theactualflowwillgetlostifeachstatementhasitsownlocalizedtry/catchblock,but
youneedtogroupthingslogically.
Those of you unfamiliar with objectoriented error handling won'tbe familiar with this
particularrant.Othersmaybetiredofhearingit,butthenewbiesneedtobeeducated.Themove
tomoremodernlanguagesmeantamoveawayfromlinenumbersandGOTOstatements.While
thiswasn'ta bad thing in general, itreally waxed error handling. Mostprogrammers didn't
completelyembracetheloca lizederrorhandler methodology,andwithoutlinenumbersand
RESUMEstatementsthequalityoferrorhandlingtanked.Wehaveagoodexampleofthetypical
errorhandlingqualityItypicallyseewithJavainthisexample.Ifanystatementintherangeof
listinglines14through76throwsanexception,welandinthecatchblockwithoutanyideaof
whichstatementactuallythrewtheexception.Evenifwecouldidentifyexactlywhichlinethrew
theexception,wewouldhavenomethodofgettingbackthere.Javadoesn't
haveaRETRYor
RESUMEthatwouldallowustofixaproblemthencontinueon.
Manypeoplewilltryto characterize codelikethisasaprogrammer beinglazy,andthat
wouldbeunfair.The authorshereweretryingtoshowhowtodosomethingwithouttheerror
handlinggettingintheway. Thetroubleisthatmostoftheseexampleswillbemodifiedonly
slightlybyprogrammersnewtothefield,thendistributedtoothers.Theydon't
knowanybetter,
andcodelikethiswilleventuallycreepintoproductionsystems.
If you want to be even more unfair you can also point out that catching the universal
Exceptionclassasisdoneatlistingline77isnowlistedasabad/undesirablepracticebySun.
Lotsofcodecurrentlyinproductiondoesthis. Theproblemwithdoingthisisthatyoumask
reallyhardruntimeerrors(likeadiskfailureorbadRAM)whichreallyshouldn'tbemasked.Not
onlyistherenothingyoucandoabouttheminyourprogram,thesystemmanagerneedstoknow
aboutthemASAP!

Chapter1Fundamentals

40

Part of the desire for a clean and simple source listing came from the early days of
programming. Classicallytrainedprogrammers learnedstructuredanalysisanddesign. More
importantly, the first language they learned was BASIC. Later versions of BASIC removed
nearlyalllinenumbersfromthelanguage.Thismigrationmadethelanguagenearlyuseless.The
movetolocalizederrorhandlingwithWHENERRORIN...USE...ENDWHENconstructs
prettymuchruinedthelanguageforbusinessuse.Itallcameaboutbecausealotofpeopletrying
tolearnthelanguageeitherrefusedtokeepeitheraprintoutbytheirsideortwoeditwindows
open.
One of the very first executable lines you would find in most BASIC modules read as
follows:
99

ON ERROR GOTO 32000

! old style error handling

Otherthancheckingafunctionreturnvalue,noothererrorhandlingexistedinthesource
untilyougottoBASICline32000.
32000 !;;;;;;;;;;
!
Old style error handling
!;;;;;;;;;;
SELECT ERL
CASE = 910%
L_ERR% = ERR
PRINT "Unable to open input file"; drawing_data$
PRINT "Error: ";L_ERR%;" ";ERT$( L_ERR%)
RESUME 929
CASE = 912%
L_ERR% = ERR
PRINT "Unable to open report file "; rpt_file$
PRINT "Error: ";L_ERR%;" ";ERT$( L_ERR%)
RESUME 929
CASE = 930%
PRINT "Invalid input"
PRINT "Please re-enter"
RESUME 930
CASE = 940%
L_ERR% = ERR
PRINT "Unable to retrieve record GE |";BEG_DATE$;"|"
PRINT "Error: ";L_ERR%;" ";ERT$( L_ERR%)
RESUME 949
CASE = 942%
B_EOF% = 1%
IF ERR <> 11%
THEN
L_ERR% = ERR
PRINT "Unable to fetch next input record"
PRINT "Error: ";L_ERR%;" ";ERT$( L_ERR%)
END IF

Chapter1Fundamentals

41

RESUME 942
CASE ELSE
ON ERROR GOTO 0
END SELECT
32767 ! End of module
PROGRAM_EXIT:

I'llbe the first to admit that this SELECT statement used to get out of hand. Some
programmers refused touseaSELECTsoyouhad anugly seriesofnestedIFTHENELSE
statements. Itdid,however,leavethelogicflowcleanandapparent(ifyouwereacompetent
programmer)anditallowedyoutohandlejustabouteveryerroryoucouldpotentiallyrecover
from.RESUMEandRETRYallowedustoreturnprogramcontroltoanylinenumberorlabelin
theprogram.Someabusedit,forcertain,butthepowerandgraceofthistechnologyislacking
fromallOOPerrorhandlingtoday.
EverybodywantsthecleanlookthatBASICwitholdstyleerrorhandlinghad,somostJava
programshavenousableerrorhandling.
1.5 rollie1.j
rollie1.jaava
Quitesimply, wearegoing totakeexample1.java,fixafewthings,thenadd someprint
functionality.Imuststressthatthisisn't
agreatexample,butitisaverycommondesign.Ihave
encounteredthissamedesigntimeandtimeagain,nomatterwhatlanguageorxBASElibrarywas
beingused.
rollie1.java
1)
2)
3)
4)
5)
6)
7)
8)
9)
10)
11)
12)
13)
14)
15)
16)
17)
18)
19)
20)
21)
22)
23)
24)

import
import
import
import
import

java.io.*;
java.util.*;
org.xBaseJ.*;
org.xBaseJ.fields.*;
org.xBaseJ.Util.*;

public class rollie1 {


// variables used by the class
//
private DBF aDB = null;
private CharField classId = null;
private CharField className = null;
private CharField teacherId = null;
private CharField daysMeet = null;
private CharField timeMeet = null;
private NumField credits = null;
private LogicalField UnderGrad = null;
private boolean continue_flg = true;
//;;;;;;;;;;
// Main module
//;;;;;;;;;;

Chapter1Fundamentals

42
25)
26)
27)
28)
29)
30)
31)
32)
33)
34)
35)
36)
37)
38)
39)
40)
41)
42)
43)
44)
45)
46)
47)
48)
49)
50)
51)
52)
53)
54)
55)
56)
57)
58)
59)
60)
61)
62)
63)
64)
65)
66)
67)
68)
69)
70)
71)
72)
73)
74)
75)
76)
77)
78)
79)
80)
81)
82)
83)
84)
85)
86)
87)

public void do_it(){


try{
//
// You must set this unless you want NULL bytes padding out
// character fields.
//
Util.setxBaseJProperty("fieldFilledWithSpaces","true");
open_database();

// use an existing database if possible

if (!continue_flg) {
continue_flg = true;
create_database(); // if none exists create
if (continue_flg)
add_rows();
} // end test for successful open of existing database
//;;;;;
// You cannot just blindly run the report.
// We could have tried to create a database on a full disk
// or encountered some other kind of error
//;;;;;
if ( continue_flg) {
dump_records();
dump_records_by_primary();
dump_records_by_secondary();
aDB.close();
} // end test for open database
}catch(IOException i){
i.printStackTrace();
} // end catch
} // end do_it
//;;;;;;;;;;
// method to add some rows
//
// Notice that I added the rows in reverse order so we could
// tell if the unique index worked
//;;;;;;;;;;
private void add_rows() {
try {
classId.put("JAVA501");
className.put("JAVA And Abstract Algebra");
teacherId.put("120120120");
daysMeet.put("NNYNYNN");
timeMeet.put("0930");
credits.put(6);
UnderGrad.put(false);
aDB.write();
classId.put("JAVA10200");
className.put("Intermediate JAVA");
teacherId.put("300020000");
daysMeet.put("NYNYNYN");
timeMeet.put("0930");
credits.put(3);
UnderGrad.put(true);
aDB.write();

Chapter1Fundamentals
88)
89)
90)
91)
92)
93)
94)
95)
96)
97)
98)
99)
100)
101)
102)
103)
104)
105)
106)
107)
108)
109)
110)
111)
112)
113)
114)
115)
116)
117)

43

classId.put("JAVA10100");
className.put("Introduction to JAVA");
teacherId.put("120120120");
daysMeet.put("NYNYNYN");
timeMeet.put("0800");
credits.put(3);
UnderGrad.put(true);
aDB.write();

} catch( xBaseJException j){


j.printStackTrace();
continue_flg = false;
} // end catch xBaseJException
catch( IOException i){
i.printStackTrace();
} // end catch IOException
// end add_rows method

//;;;;;;;;;;
//
Method to create a shiny new database
//;;;;;;;;;;
private void create_database() {
try {
//Create a new dbf file
aDB=new DBF("class.dbf",true);
attach_fields(true);

aDB.createIndex("classId.ndx","classId",true,true);
// true delete ndx, true - unique index,
118)
aDB.createIndex("TchrClass.ndx","teacherID+classId", true, false);
//true - delete NDX, false - unique index,
119)
System.out.println("created database and index files");
120)
121)
} catch( xBaseJException j){
122)
j.printStackTrace();
123)
continue_flg = false;
124)
} // end catch
125)
catch( IOException i){
126)
i.printStackTrace();
127)
} // end catch IOException
128)
} // end create_database method
129)
130)
//;;;;;;;;;;
131)
//
Method to open an existing database and attach primary key
132)
//;;;;;;;;;;
133)
public void open_database() {
134)
try {
135)
//Create a new dbf file
136)
aDB=new DBF("class.dbf");
137)
138)
attach_fields( false);
139)
140)
aDB.useIndex("classId.ndx");
141)
System.out.println("opened database and primary index");
142)
} catch( xBaseJException j){
143)
continue_flg = false;
144)
} // end catch
145)
catch( IOException i){
146)
continue_flg = false;
147)
} // end catch IOException
148)
} // end open_database method

Chapter1Fundamentals

44
149)
150)
151)
152)
153)
154)
155)
156)
157)
158)
159)
160)
161)
162)
163)
164)
165)
166)
167)
168)
169)
170)
171)
172)
173)
174)
175)
176)
177)
178)
179)
180)
181)
182)
183)
184)
185)
186)
187)
188)
189)
190)
191)
192)
193)
194)
195)
196)
197)
198)
199)
200)
201)
202)
203)
204)
205)
206)
207)
208)
209)
210)
211)

//;;;;;;;;;;
//
Method to populate known class level field objects.
//
This was split out into its own method so it could be used
//
by either the open or the create.
//;;;;;;;;;;
private void attach_fields( boolean created_flg) {
try {
if ( created_flg) {
//Create the fields
classId
= new CharField("classId",9);
className
= new CharField("className",25);
teacherId
= new CharField("teacherId",9);
daysMeet
= new CharField("daysMeet",7);
timeMeet
= new CharField("timeMeet",4);
credits
= new NumField("credits",2, 0);
UnderGrad
= new LogicalField("UnderGrad");
//Add field definitions to database
aDB.addField(classId);
aDB.addField(className);
aDB.addField(teacherId);
aDB.addField(daysMeet);
aDB.addField(timeMeet);
aDB.addField(credits);
aDB.addField(UnderGrad);
} else {
classId
className
teacherId
daysMeet
timeMeet
credits
UnderGrad
}

=
=
=
=
=
=
=

(CharField) aDB.getField("classId");
(CharField) aDB.getField("className");
(CharField) aDB.getField("teacherId");
(CharField) aDB.getField("daysMeet");
(CharField) aDB.getField("timeMeet");
(NumField) aDB.getField("credits");
(LogicalField) aDB.getField("UnderGrad");

} catch ( xBaseJException j){


j.printStackTrace();
} // end catch
catch( IOException i){
i.printStackTrace();
} // end catch IOException
} // end attach_fields method
//;;;;;;;;;;
//
Method to test private flag
//;;;;;;;;;;
public boolean ok_to_continue() {
return continue_flg;
} // end ok_to_continue method
//;;;;;;;;;;
//
Method to dump records by record number
//;;;;;;;;;;
public void dump_records() {
System.out.println( "\n\nRecords in the order they were entered\n");
System.out.println( "classId
className
" +
"teacherId daysMeet time cr UnderGrad");
for (int x=1; x <= aDB.getRecordCount(); x++) {
try {

Chapter1Fundamentals
212)
213)
214)
215)
216)
217)
218)
219)
220)
221)
222)
223)
224)
225)
226)
227)
228)
229)
230)
231)
232)
233)
234)
235)
236)
237)
238)
239)
240)
241)
242)
243)
244)
245)
246)
247)
248)
249)
250)
251)
252)
253)
254)
255)
256)
257)
258)
259)
260)
261)
262)
263)
264)
265)
266)
267)
268)
269)
270)
271)
272)
273)
274)

45

aDB.gotoRecord( x);
}
catch( xBaseJException j){
j.printStackTrace();
} // end catch IOException
catch( IOException i){
i.printStackTrace();
} // end catch IOException
System.out.println( classId.get() + " " + className.get() +
" " + teacherId.get() + " " + daysMeet.get() + " " +
timeMeet.get() + " " + credits.get() + " " +
UnderGrad.get());
} // end for x loop
} // end dump_records method
//;;;;;;;;;;
//
Method to dump records via primary key
//;;;;;;;;;;
public void dump_records_by_primary() {
System.out.println( "\n\nRecords in primary key order\n");
System.out.println( "classId
className
" +
"teacherId daysMeet time cr UnderGrad");
try {
aDB.useIndex("classId.ndx");
continue_flg = true;
aDB.startTop();
while( continue_flg) {
aDB.findNext();
System.out.println( classId.get() + "
className.get() + " " +
teacherId.get() + " " +
daysMeet.get() + " " +
timeMeet.get() + " " +
credits.get() + " " +
UnderGrad.get());

" +

} // end while loop


}
catch( xBaseJException j) {
continue_flg = false;
}
catch( IOException i) {
continue_flg = false;
}
}

// end dump_records_by_primary method

//;;;;;;;;;;
//
Method to dump records off by secondary key
//;;;;;;;;;;
public void dump_records_by_secondary() {
System.out.println( "\n\nRecords in secondary key order\n");
System.out.println( "classId
className
" +
"teacherId daysMeet time cr UnderGrad");
try {
aDB.useIndex("TchrClass.ndx");
continue_flg = true;

Chapter1Fundamentals

46
275)
aDB.startTop();
276)
277)
while( continue_flg) {
278)
aDB.findNext();
279)
280)
System.out.println( classId.get() + "
281)
className.get() + " " +
282)
teacherId.get() + " " +
283)
daysMeet.get() + " " +
284)
timeMeet.get() + " " +
285)
credits.get() + " " +
286)
UnderGrad.get());
287)
288)
} // end while loop
289)
}
290)
catch( xBaseJException j) {
291)
continue_flg = false;
292)
}
293)
catch( IOException i) {
294)
continue_flg = false;
295)
}
296)
297)
} // end dump_records_by_secondary method
298) } // end class rollie1

" +

ThefirstthingyouwillnoticeaboutthisexampleisthatIrippedoutthemain()method.Most
peoplewritingJavaexamplestrytogetbywithasinglesourcefileexample,evenwhentheyare
usingacomplexlibraryordatabasesystem. I'm
nowherenearOne WiththeObject levelof
OOPwiththisdesign,butitistypicalofthingsyouwillencounterinthefield.
Thisdesignworkswhenyouhavecreatedasinglefiledatabasewhichistobeusedbyone
andonlyoneapplication. Thisdesignfailsassoonasyouneedtousethatsamedatabasein
anotherapplication. Whenyouenterashopthathadaprogrammerwholikedthisdesign,you
willusuallybeenteringafterthatprogrammerhasleft(orwasaskedtoleave).Whenyouneedto
addadditionalfunctionalityyoueitherhavetocutandpastelargechunksofcodeoutofthisclass
intoanewone,oryouwatchthisclassgrowtobehundredsofthousandsoflinesofsource.
One of the many things I don'tlike about Java is its lack of header files. Most Java
developersendupusingsomekindofIDElikeEclipse,notbecauseit'sa
goodeditor,butbecause
ithasbuiltinJavaspecificfunctionalitywhichwillcreateviewsofallthemethodsandmembers
inaclassifyouloadthecorrectplugin. InC++wehadheaderfiles inwhich theclasswas
prototypedandyoucouldeasilyseeallofitsmethodsandmembers.Thissourcefileisjustshyof
300 lines in length, and if I didn'tprefix my methods with a comment containing ten ;
charactersyouwouldhavetroublelocatingthem. Imaginewhatitislikewhenthelistingis
12,000lineslong.

Chapter1Fundamentals

47

Allinstancesofthedatabaseandcolumnnamesaremovedouttotheclasslevelinthisclass.
Doingsoallowsthemtobesharedbyallmethodsintheclass.Iflaggedthemasprivatesoothers
couldn'ttouchthemfromoutsidetheclass.
Listing line 25 is where the public method do_it() begins. This is really the whole
application.Theflowwouldbealittlebiteasiertoreadifwedidn't
havetokeepcheckingthe
continue_flgvariable,orifJavaallowedstatementmodifierslikeDECBASICdid:
GOSUB C2000_PAGE_HEADING

IF LINE_CNT% >= page_size%

A lot of people complained about statement modifiers, but those people never wrote
productionsystems.Eventually,BASICbecametheonlysurvivingcommerciallanguagetohave
thissyntax.Theflowofthisparticularmethodwouldcleanupconsiderablyifwecouldusesuch
syntax.
Evenwiththecumbersomeifstatements,youshouldbeabletoascertaintheflowofthe
method.Firstwetrytouseanexistingdatabase.Ifthatfails,wecreatethedatabase.Ifdatabase
creationwassuccessful,weaddsomedatatothedatabase.Oncewehavesuccessfullyestablished
adatabase,wereportoffthedatainthreedifferentsortorders,closethedatabase,andexit.
Pleasetakenoticeoflistinglines67,78,and88.Theselinesassigntheprimarykeyvaluesto
each row that wewillbeadding. Whatyou need to notice isthat I stored theserecords in
descendingorderbyprimarykey.Havingdatawhichwasaddedinaknownsortorderiscritical
tounderstandingwhetherourreportsworkedcorrectlyornot.
Bothcreate_database()andopen_database()callamethodnamedattach_fields(). Wehave
verylittletodiscussinthecreate_database()methodsincemuchofthecodewasstolenfrom
example1.java.Youwillnoticethatinopen_database()wedon't
providethetrue parameterto
theDBFconstructor. ItisthisparameterwhichtellstheDBFconstructorwhether tousean
existingdatabaseorcreateanewone.
Noticeatlistingline140thatwedon't
createanindex,butratherusetheexistingindexfile.
Using an existing index file can be an incredibly dangerous thing to do when working with
xBASEfiles.Attemptingtocreateashinynewindexfileusingthesamehardcodednameaslast
timecanalsobeadangerousthingasanotherusermayhavethefileopened,whichmeansyour
processwillfail.DuringthedarkdaysofDOSitwasalmostimpossibletogenerateauniquefile
name every time. The 8.3 naming schema was pretty restrictive. Not many of your disk
partitionswillbeFAT16thesedays,though.FAT32cameontothescenein1996withWindows
95OSR2.FloppydiskdriveswillstilluseFAT16,butmostofthesuper floppy disks(120and
240Meg)willuseFAT32orsomethingelse,whichallowsforverylongfilenames.

48

Chapter1Fundamentals

IntheDOSdays,mostxBASElibrariesdidn't
haveareindex()function.Theywereallbusy
tryingtobemultiuserandtheresimplywasn't
agoodmultiusermethodofrebuildinganindex
whileotherusers had thefile open. (There reallyisn'teven today.) We alsodidn'thave a
universaltemporarydirectory.Thereweresomeenvironmentvariablesyoucouldhopewereset
(TMP,TEMP,etc.),butallinall,youwereonyourown.
FewthingswouldcausemoreproblemsinxBASEsoftwarethanoneprogrammerforgetting
toopentheproduc tion indexwhentheyaddedrecordstothedatabase.Anyapplicationwhich
usedtheproduction indextoaccessrecordswouldsimplyskipprocessinganyrecordsin the
databasewhichdidn'thaveanindex.
Inafeatofpurelydefensivecoding,mostprogrammerswouldtakeastabatgeneratinga
uniquefilenamefortheindex,thencreatetheneededindexaftertheyopenedthedatabase.When
youhadthousandsofrecordsonthoseoldandslow40Megharddrives,itcouldtakeminutesfor
thefirstscreentoload,butatleastyouknewyouwereprocessingallofthedata...ordidyou?
Nobody elseknewaboutyourshinynewindexedfile. Thismeanstheyweren'tbothering to
updateanyentriesinitwhiletheywereaddingrecordstothedatabase.Thelackofacommon
OSenforcedtemporarydirectoryledtoalotofpoliciesandproceduresconcerningwhatfilesto
delete when. More than one shop blew away their production index while trying to delete
temporaryindexfilestofreeupspace.
Some shops learned to live with and work around the pitfalls. They put policies and
proceduresinplacesousersdidn't
havetowaitentireminutesforthefirstapplicationscreento
displaydata.TheworldofxBASEeventuallycreatedtheMDXfileinanattempttosolvethese
issues.WewilldiscusstheMDXfileinalaterexample.
Listinglines159through183showabitofdifferencebetweencreatinganewfileandusing
anexistingfile.Whenthedatabaseisshinyandnew,youmustcreatethecolumnobjects,then
addthemtothedatabaseobject.Theactofaddingthemtotheobjectactuallycreatesthecolumns
inthedatabase.Whenyouareusinganexistingdatabaseyoumustpullthefielddefinitionsoutof
thedatabaseobject. Ifyoucreatefielddefinitionsandattempttoaddthem,theywillbenew
columns,unlesstheyhaveamatchingcolumnnamealreadyinthedatabase,thenanexception
willbethrown.
OnethingIwouldhavelikedtoseeninthelibrarywasaseriesofge t methods,onefor
eachsupporteddatatype.Thiswouldmoveanycastinginsideofaclassmethod.ManyoftheC/
C++librariesIusedovertheyearshadthisfunctionalitytokeepcodeascastfreeaspossible.It
wouldbenicetocallamethodnamedaDB.getCharField(classId) andhaveiteitherreturna
CharFieldobjectorthrowanexception.Ofcourse,itwouldalsobeniceiftheexceptioncould

Chapter1Fundamentals

49

haveactualerrorcodeswhichtoldyouwhattheexceptionwas,notjustthatithappenedtohave
died.
Thedump_records()methodstartingonlistingline205doesn't
havemuchcomplexitytoit.I
useasimpleforlooptoreadfrom1tothemaximumnumberofrecordsinthedatabase,printing
eachrecordout.ThemethodgetRecordCount()returnsthecurrentrecordcountinthedatabase.
ThemethodgotoRecord()physicallyreadsthatrecordnumberfromthedatabase.Youmay
recall that I told you xBASE is a relative file format. All relative file formats are actually
accessedbyrecordnumber. Theindexfilesarereallystoringakeyvalueandcorresponding
recordnumberinaBtree(binarytree)fashion.Thismethodwalksthroughtherecordsasthey
werewrittentothedatafilewithoutpayinganyattentiontokeyvalues.
Atlistingline239,Ishowyouhowtoclearthec urrentrecord valuestoredinternallyinthe
class.ThemethodstartTop()willsetthecurrentrecordvaluetozeroandmovetheindexpointer
backtotherootofthecurrentlyactiveindex.
Mostofyouwouldhavetriedtouseread()insteadoffindNext()atlistingline241. Iwill
admitthatonceIreadthecommentsinthesourcefile,Igaveitawhirlaswell.Itbehavedthe
wayIthoughtitwould.AnyofyouwhohavereadThe MinimumYouNeedtoKnowtoBean
OpenVMSApplicationDeveloper ISBN139780977086603wouldhaveexpectedittonot
work as well. There is a problem with most read and readNext type functions in most
languages. Youmustfirstestablishakeyofreference viasomeotherIOoperationbeforean
ordinaryreadorreadNexttypefunctionwillwork.FindandfindNexttypemethodsarealmost
alwayssetuptofindakeyvalueequal toorgreaterthan thevaluetheycurrentlyhaveinsome
designatedkeybuffer.Ifthatbufferisnull,theytendtofindthefirstrecordinthefileviathe
currentlyactiveindex.

Pleasenote: ThetechniqueI'veshown you here willworkwith xBaseJand itsdBASE


implementations. findNext()doesnotlookatakeyvalue,onlythepositionoftheindextree
being traversed in memory. find() actually attempts to locate a value based on key. Some
librarieshavestoredsomenumerickeysasbinaryintegers.Onmostplatformsanintegerzerois
anullvalueinbinaryintegerform.Thisnullvalueisgreaterthananegativevalueduetotheway
thesignbitistreated.YougetluckywithmanyIEEEstandardssincethereisusuallyatleastone
bitsettoindicatethenumericbaseorsomeotheraspect.
Ourmethoddump_records_by_primary()hastospecifytheprimarytocontrolsortorder.If
yourelyonsomeotherlogicpathtosetthekey,thenyoursortordermightappearrandom.Other
than the heading and the changing of the index there really is no difference between
dump_records_by_secondary()anddump_records_by_primary().

Chapter1Fundamentals

50

Noticeineachofthereportmethodsthatwehavetocalltheget()methodforeachfieldin
ordertoobtainitsvalue.Wedonothavedirectaccesstothedatavaluesinthislibrary.Some
othersallowfordirectretrieval andsomedon't.Idon'treallyhave apreferencethesedays.
DuringmyDOSprogramming daysIalwayswantedtouseClibraries,whichalloweddirect
accesstothevalues.Thiswasn't
becauseIwasanUbergeektryingtobeonewiththeCPU,but
becauseofthewonderful640Kmemorylimitationoftheday.IfIallocatedthestorageforthe
returnedvalues,IcouldputitinanEMSpagewhichcouldbeswappedoutondemand.Most
vendorsofthirdpartylibrariesrefusedtoprovideanysupportifyouwereswappingtheircodein
andoutofthelower640Kviaanoverlaylinker.
Compilingandrunning thisthingisn't
abigchallenge,assumingyou'vealreadygotyour
CLASSPATHenvironmentvariableset.
roland@logikaldesktop:~/fuelsurcharge2$ rm class.dbf
roland@logikaldesktop:~/fuelsurcharge2$ rm teacher.dbf
roland@logikaldesktop:~/fuelsurcharge2$ java testRollie1
created database and index files
Records in the order they were entered
classId
JAVA501
JAVA10200
JAVA10100

className
teacherId daysMeet
JAVA And Abstract Algebra 120120120 NNYNYNN
Intermediate JAVA
300020000 NYNYNYN
Introduction to JAVA
120120120 NYNYNYN

time
0930
0930
0800

cr
6
3
3

UnderGrad
F
T
T

time
0800
0930
0930

cr
3
3
6

UnderGrad
T
T
F

time
0800
0930
0930

cr
3
6
3

UnderGrad
T
F
T

Records in primary key order


classId
JAVA10100
JAVA10200
JAVA501

className
teacherId daysMeet
Introduction to JAVA
120120120 NYNYNYN
Intermediate JAVA
300020000 NYNYNYN
JAVA And Abstract Algebra 120120120 NNYNYNN

Records in secondary key order


classId
JAVA10100
JAVA501
JAVA10200

className
teacherId daysMeet
Introduction to JAVA
120120120 NYNYNYN
JAVA And Abstract Algebra 120120120 NNYNYNN
Intermediate JAVA
300020000 NYNYNYN

Ideletedtheexistingdatafileandindexbyhandsoyoucouldseetheresultofafirstrun
situation.Youwillalsowanttodothisifyouhavecompiledandruntheexample1.javaprogram.
Thisparticularsetoftestdataisreordered.Ifyourunitagainsttheoriginaldatafile,youwon't
seeanydifferencesbetweenthefirstandthesecondreport.
Justtobecomplete,letmeshowyouthesimplelittletestsource.

Chapter1Fundamentals

51

testRollie1.java
1) public class testRollie1 {
2)
3)
public static void main(String args[]){
4)
rollie1 r = new rollie1();
5)
6)
r.do_it();
7)
8)
} // end main method
9)
10) } // end class testRollie1

1.6 Progra
mming
As
sign
ment
ogram
mingAs
Assign
signm
ent1
Modifyrollie1.javatoremovetheopeningoftheprimarykeyfilewhenopeningtheexisting
database. Replace dump_records_by_primary() and dump_records_by_secondary() with one
methoddump_records_by_key() whichacceptsaStringparameter thatistheindexfilename.
Compileandrunyourprogram.Testitwithbothavalidfilenameandanonexistentfilename.
1.7 SizeMatters
Iknow,thatsectionheadingmakesitsoundlikeI'm
goingtobesellinggymequipmentor
maleenhancementtablets,butitreallyistruewithxBaseJ:sizereallydoesmatter.Itisyourjob
to ensure your application doesn'toverrun a numeric field. Character fields will throw an
exception,butnumericfieldswillnot.
example5.java
1)
2)
3)
4)
5)
6)
7)
8)
9)
10)
11)
12)
13)
14)
15)
16)
17)
18)
19)
20)
21)
22)
23)
24)
25)
26)

import
import
import
import
import

java.io.*;
java.util.*;
org.xBaseJ.*;
org.xBaseJ.fields.*;
org.xBaseJ.Util.*;

public class example5 {


public static void main(String args[]){
try{
//
// You must set this unless you want NULL bytes padding out
// character fields.
//
Util.setxBaseJProperty("fieldFilledWithSpaces","true");
//Create a new dbf file
DBF aDB=new DBF("roi.dbf",true);
//Create the fields
NumField pctrtn = new NumField("pctrtn",6, 3);
CharField fundnm = new CharField("fundnm",20);
NumField invstamt = new NumField("invstamt", 15,2);

Chapter1Fundamentals

52
27)
28)
29)
30)
31)
32)
33)
34)

//Add field definitions to database


aDB.addField(pctrtn);
aDB.addField(fundnm);
aDB.addField(invstamt);

aDB.createIndex("roik0.ndx","pctrtn",true,true);
// true delete ndx, true - unique index,
35)
System.out.println("\nindex created ... now adding records");
36)
37)
fundnm.put("LargeCap");
38)
pctrtn.put(-4.5);
39)
invstamt.put(550000);
40)
aDB.write();
41)
42)
fundnm.put("MidCap");
43)
pctrtn.put(2.3);
44)
invstamt.put(120000);
45)
aDB.write();
46)
47)
fundnm.put("Growth");
48)
pctrtn.put(3.4);
49)
invstamt.put(45000000);
50)
aDB.write();
51)
52)
fundnm.put("SmallCap");
53)
pctrtn.put(-6.2);
54)
invstamt.put(23000000000.0);
55)
aDB.write();
56)
57)
fundnm.put("Spyder");
58)
pctrtn.put(2);
59)
invstamt.put(78923425);
60)
aDB.write();
61)
62)
fundnm.put("PennyStk");
63)
pctrtn.put(26.5);
64)
invstamt.put(888000);
65)
aDB.write();
66)
67)
fundnm.put("BioTech");
68)
pctrtn.put(-34.6);
69)
invstamt.put(345567.89);
70)
aDB.write();
71)
72)
System.out.println( "Records added\n");
73)
System.out.println( "ROI
Fund
Amount");
74)
System.out.println( "-------------------------------");
75)
76)
aDB.startTop();
77)
for( int i=0; i < aDB.getRecordCount(); i++)
78)
{
79)
aDB.findNext();
80)
System.out.println( pctrtn.get() + "
" + fundnm.get() +
81)
invstamt.get());
82)
}
83)
84)
}catch(Exception e){
85)
e.printStackTrace();
86)
}
87) }
88) }

Chapter1Fundamentals

53

Noticeatlistingline24thatIdeclarepctrtntobesixlongwiththreedecimalplaces.Ithen
goaheadandmakethisanindexforthedatafile.Atlistingline68Iassignthevalue34.6tothe
field.Itseemsinnocentenough,doesn'tit?Let'stakealookatwhathappens.
roland@logikaldesktop:~/fuelsurcharge2$ javac example5.java
roland@logikaldesktop:~/fuelsurcharge2$ java example5
index created
Records added
ROI
------6.200
-4.600
-4.500
2.000
2.300
3.400
26.500

...

now adding records

Fund
-------------------SmallCap
BioTech
LargeCap
Spyder
MidCap
Growth
PennyStk

Amount
------23000000000.00
345567.89
550000.00
78923425.00
120000.00
45000000.00
888000.00

TakealookatwhereourBioTechrecordendedup.That's
notthevalueweassigned,isit?
Therewasnoexceptionthrownwhenwerantheprogram;wesimplygotthewrongvaluestored.
1.8 Progra
mming
As
sign
ment
ogram
mingAs
Assign
signm
ent2
Modifyexample5.javabyexpandingthesizeoftheROIcolumnandtrytoaddarecordwith
afundnamegreaterthan20characters.
1.9 Exami
nin
Examin
ingga
aDBF
Fromthe1960sthroughmuchofthe1980s,softwarevendorstriedtolockpeopleintotheir
productlinesinavarietyofways.Oneofthemosttriedandtruemethodswastocreateyourown
proprietarydatafileformat.Evenifyouusedtheindexedfilesystemprovidedbythecomputer
operating system,aslongasyoudidn'tcoughuptherecordlayouts, your customerscouldn't
accesstheirdatawithoutusingyoursoftwareand/orbuyingadditionalservicesfromyou.Given
thatMBAsarecreatureswhogotoschooltohaveboththeirethicsandsoulremovedsotheycan
runabusinessinthemostprofitablemethodpossible,thefeeskeptgoingupandthelawyerskept
gettingricheroverbreachofcontractlawsuits.
AshtonTatecertainlytried togothatroutewithdBASE,buttherewasalotofexisting
technologyoutthereforpeopletoworkwith.Thelawyersandtheattitudecontinuedtoturnall
potentialnew customers against Ashton Tateand theother xBASEplatforms gainedground.
Ultimately,therewerequiteafewthingsthatdidAshtonTatein.Firstoff,allofthexBASEfile
formatsstoredthefilelayoutinformationintheheader.Allyouhadtodowasfigureouthowto
parsetheheaderandyoucouldgettomostofthedata.Second,Vulcan,theoriginalversionof

Chapter1Fundamentals

54

whatbecamedBASEII,wasn't
releasedasacommercialproduct. Wedidn't
havetheInternet
backthen,butwehadBBSnetworkswhichparticipatedinechorelaysandgaveaccesscreditfor
fileuploads.OnceVulcanmadeittoacoupleofthelargerboards,itwaseverywhereinundera
month.Thisgavenearlyeverycompetingproductthesamestartingpoint.
Giventhememoryrestrictionsoftheday,AshtonTateandothersdidn't
havetheoptionof
hidingalloftheinformationinsomeencryptedformatandrequiringanenginetoberunninglike
MySQL,Oracle,oranyoftheotherdatabaseenginesoftoday. TheJetPropulsionLaboratory
wasn'tin
thebusinessofputtingoutcommercialsoftware.Theysimplyhadasevereneedtostore
datainsomeindexedformatforreportingpurposes.Aneedsoseverethatsomeonewasallowed
totakehowevermuchtimeittookthemtosolvetheproblem.Theychosetosolvetheproblemin
themosteasilysupportablemeansavailabletothematthetime.
Ifyouaren'tlong
inthetoothlikemyself,youprobablydon't
understandjusthoweasyitisto
supportthexBASEformat.Ournextexampleshouldgiveyousomeidea.
showMe.java
1)
2)
3)
4)
5)
6)
7)
8)
9)
10)
11)
12)
13)
14)
15)
16)
17)
18)
19)
20)
21)
22)
23)
24)
25)
26)
27)
28)
29)
30)
31)
32)
33)
34)
35)
36)
37)

import
import
import
import
import
import

java.io.*;
java.util.*;
java.text.*;
org.xBaseJ.*;
org.xBaseJ.fields.*;
org.xBaseJ.Util.*;

public class showMe {


public static final int MAX_NAME_LEN = 11;
// variables used by the class
//
private DBF aDB = null;
private boolean continue_flg = true;
//;;;;;;;;;;
// Main module
//;;;;;;;;;;
public void showDBF( String _dbfName){
try{
aDB = new DBF( _dbfName);
} catch( xBaseJException j){
System.out.println( "Unable to open " + _dbfName);
} // end catch xBaseJException
catch( IOException i){
System.out.println( "Unable to open " + _dbfName);
} // end catch IOException
System.out.println( "\n" + _dbfName + " has:");
System.out.println( "
" + aDB.getRecordCount() + " records");
System.out.println( "
" + aDB.getFieldCount() + " fields\n");
System.out.println( "
FIELDS");
System.out.println( "Name
" +
"Type Length Decimals");
System.out.println( "-----------------------------" +

Chapter1Fundamentals
38)
39)
40)
41)
42)
43)
44)
45)
46)
47)
48)
49)
50)
51)
52)
53)
54)
55)
56)
57)
58)
59)
60)
61)
62)
63)
64)
65)
66)
67)
68)
69)
70)
71)
72)
73)
74)
75)
76)
77)
78)
79)
80)
81)
82)
83)
84)
85)
86)
87)
88)
89)
90)
91)
92)
93)
94)
95)
96)
97)
98)
99)
100)

55

"----------------------");
StringBuilder sb = new StringBuilder();
Formatter r = new Formatter( sb, Locale.US);
for( int i=1; i <= aDB.getFieldCount(); i++) {
try {
Field f = aDB.getField(i);
r.format( " %-25s
%1c
%4d
%4d\n",
f.getName(),
f.getType(),
f.getLength(),
f.getDecimalPositionCount());
} catch( xBaseJException x) {
System.out.println( "Error obtaining field info");
}
}

// end for loop

System.out.println( r.toString());
try {
aDB.close();
} catch( IOException o) {}
} // end showDBF method
//;;;;;;;;;;
// Method to dump all records in database
//;;;;;;;;;;
public void dump_records( String _dbfName) {
dump_records( _dbfName, -1);
} // end dump_records method
//;;;;;;;;;;
// Method to dump first N records from database.
//;;;;;;;;;;
public void dump_records( String _dbfName, int _reccount) {
int the_count = 0;
if (_reccount < 1) {
try{
aDB = new DBF( _dbfName);
the_count = aDB.getRecordCount();
aDB.close();
} catch( xBaseJException j){
System.out.println( "Unable to open " + _dbfName);
} // end catch xBaseJException
catch( IOException i){
System.out.println( "Unable to open " + _dbfName);
} // end catch IOException
} else {
the_count = _reccount;
} // end test for negative _reccount parameter
}

dump_records( _dbfName, 1, the_count);


// end dump_records method

//;;;;;;;;;;
// Method to dump a range of records from start to end.
//;;;;;;;;;;
public void dump_records( String _dbfName, int _startRec,
int l_x=0;

int _endRec) {

Chapter1Fundamentals

56
101)
102)
103)
104)
105)
106)
107)
108)
109)
110)
111)
112)
113)
114)
115)
116)
117)
118)
119)
120)
121)
122)
123)
124)
125)
126)
127)
128)
129)
130)
131)
132)
133)
134)
135)
136)
137)
138)
139)
140)
141)
142)
143)
144)
145)
146)
147)
148)
149)
150)
151)
152)
153)
154)
155)
156)
157)
158)
159)
160)
161)
162)
163)

int curr_width=0;
StringBuilder sb = new StringBuilder();
Formatter r = new Formatter( sb, Locale.US);
try {

aDB = new DBF( _dbfName);


} catch( xBaseJException j){
System.out.println( "Unable to open " + _dbfName);
} // end catch xBaseJException
catch( IOException i){
System.out.println( "Unable to open " + _dbfName);
} // end catch IOException

try {
int field_count = aDB.getFieldCount();
int heading_length = 0;
String dash_line = "";
for (int i=1; i <= field_count; i++) {
int fld_width = MAX_NAME_LEN;
int x;
Field f = aDB.getField(i);
String namStr = f.getName();
x = (fld_width > f.getLength()) ? fld_width : f.getLength();
String s8 = "%-" + x + "s ";
r.format( s8, namStr);
//
// I have never understood how Java could be declared
// so advanced by so many and the language not
// include something as fundamental as the STRING$()
// function from BASIC to generate an N length string
// of some character.
//
char[] dl = new char[ x];
Arrays.fill( dl, '-');
dash_line += new String(dl) + " ";
}

// end for loop to print headings

System.out.println( r.toString());
System.out.println( dash_line);
for (l_x=_startRec; l_x <= _endRec; l_x++) {
if (sb.length() > 0)
{
sb.delete(0, sb.length()); // nuke output buffer
}
aDB.gotoRecord( l_x);
for (int j=1; j <= field_count; j++) {
Field f = aDB.getField(j);
switch (f.getType()) {
case 'C':
CharField c = (CharField) f;
curr_width = (MAX_NAME_LEN > c.getLength()) ?
MAX_NAME_LEN : c.getLength();
String s = "%-" + curr_width + "s ";
r.format( s, c.get());
break;
case 'D':

Chapter1Fundamentals
164)
165)
166)
167)
168)
169)
170)
171)
172)
173)
174)
175)
176)
177)
178)
179)
180)
181)
182)
183)
184)
185)
186)
187)
188)
189)
190)
191)
192)
193)
194)
195)
196)
197)
198)
199)
200)
201)
202)
203)
204)
205)
206)
207)
208)
209)
210)
211)
212)
213)
214)
215)
216)
217)
218)
219)
220) }

57
DateField d = (DateField) f;
r.format( "%8s ", d.get());
break;
case 'F':
FloatField o = (FloatField) f;
curr_width = (MAX_NAME_LEN > o.getLength()) ?
MAX_NAME_LEN : o.getLength();
String s6 = "%" + curr_width + "s ";
r.format( s6, o.get());
break;
case 'L':
LogicalField l = (LogicalField) f;
curr_width = MAX_NAME_LEN;
String s1 = "%" + curr_width + "s ";
r.format( s1, l.get());
break;
case 'M':

// we don't actually go get the memo


// just print the id for it.
MemoField m = (MemoField) f;
r.format( "%10s ", m.get());
break;

case 'N':
NumField n = (NumField) f;
curr_width = (MAX_NAME_LEN > n.getLength()) ?
MAX_NAME_LEN : n.getLength();
String s2 = "%" + curr_width + "s ";
r.format( s2, n.get());
break;
case 'P':
PictureField p = (PictureField) f;
curr_width = (MAX_NAME_LEN > p.getLength()) ?
MAX_NAME_LEN : p.getLength();
String s3 = "%" + curr_width + "s ";
r.format( s3, p.get());
break;

default:
r.format("?");
} // end type switch
} // end inner for loop to print each field
System.out.println( r.toString());
// end for loop to write detail

aDB.close();
} catch( xBaseJException j){
System.out.println( "Error processing record ");
} // end catch xBaseJException
catch( IOException i){
System.out.println( "Unable to open " + _dbfName);
} // end catch IOException
} // end dump_records method
// end showMe class

58

Chapter1Fundamentals

Incasesomeofyoudon't
reallyknowanythingaboutJava,letmepointoutthatlistingline
10ishowyoudeclareaclassconstant.Thisconstantcanbereferencedfromanyapplicationeven
ifusersdon'tha
veaninstanceofthisclass,aslongastheyhaveimportedtheclassfile.Toaccess
ittheywouldsimplyneedtotypeshowMe.MAX_NAME_LENinanysourcefilewhichimports
the showMe class. The maximum name allowed by early xBASE implementations was ten
characters;avalueofelevenallowsforatrailingspace.
Listinglines22through29wereanattempttoshowyoulocalizederrorhandling. Itdoes
make the code ugly. If xBaseJException had a constructor which allowed for an integer
parameter,orbetteryet,anenumtype,wewouldn'thavetoperformlocalizedhandlingjusttotrap
foranopenerror.Ifallofthecodeisinonebigtry/catchblock,wehavenomethodoffiguring
outexactlywheretheexceptionoccurred.Yes,youcanprintthestacktrace,butifdoingsoonly
releasesaJARfilecontainingyourapplication,whatgoodisthattotheuser?
Oneofthefirstthingsthatshouldcatchyourattentionislistinglines32and33.Theheader
recordofaDBFactuallykeepstrackofboththerecordcountandthefieldcount.Weusethe
methodsprovidedbytheclasstoaccessthesevalues.
I need to get on my high horse a minute about StringBuilder and Formatter. Up until
FormatterwasaddedtotheJavalanguage,itwascompletelyunusableforbusinesspurposes.I
havebeenlambastedbymanyauselessPhDforpointingoutthatJavawasabsolutelyuselessin
thebusinessworldbecauseitwasphysicallyincapableofproducingacolumnarreport. Every
language those neverworkedintherealworld academics put down as being inferior to Java
becausetheyprecededitcould,anddid,producecolumnarreports.Youseethesereportsevery
timeyougetyourcreditcardstatement,phonebill,etc. Businesscannotfunctionwithoutthe
capabilitytoproducecolumnarreports.
TheClanguagegaveusaformatstringveryearlyon.Itevolvedovertheyearstobecome
quiteadynamicthing.Listingline46showsyouanexamplewhatweendedupwithinJava.It
resemblestheCformatstringinmanyways,butfailsinonecriticalway.TheCformatstring
allowedadevelopertousesomethinglikethefollowing:
printf( %-*s\n, curr_width, some kind of day);

Thehyphen,, forcedtheleftjustificationasitdoesinJava. Theasterisk,* ,toldthe


formattinglogictotakethenextparameterfromthelastanduseitastheWIDTHofthefield.
Thisallowedaprogrammertodocoolthingslikecenteringaheadingonapage. Itwasvery
commontoseethingslikethefollowing:
printf( %*s%s\n, 66-(strlen(heading_str)/2), , heading_str);

Chapter1Fundamentals

59

Mostbusinessreports printed on132column greenbar paper. Ifyou subtracted halfthe


lengthofthestringfromthemidpointofthelinethattoldyouroughlyhowmanyspacestoprint
infrontofthestring.
Javaisn't
quitesoprogressive.Listinglines153through156willshowyouanexampleof
thehackIhadtomaketoworkaroundthisfailure.Itisn't
pretty,butIhadtobuildadynamic
Stringwhichcontainedtheactualwidthvalueandpassthatinasthefirstparameter.
NoticealloftheinformationtheFieldclasscontains.Thereareactuallymanymoremethods;
Isimplyfocusedontheoneswewouldneed.PriorversionsofxBaseJwillnotreturnthecorrect
resultfromf.getType().Fieldisabaseclass.Thankfullywecaninstantiateit.Becauseitisa
baseclass,ithasnoknowledgeoftheclasseswhichwerederivedfromit.JavahasRTTI(Run
TimeTypeIdentification)whichkeepstrackofwhoiswhattowho,buttheclassFielddoesn't
know.IfyoutrytocallthegetType()methodoftheFieldclassinanolderversionofxBaseJ,it
willtossanexception.Iturnedinamodificationwhichallowsittoreturneitherthecorrectresult
or''toindicateanuninitializedvalue.
OlderversionsofxBaseJhaveField.javacontainingthefollowing:
/**
* @return char field type
* @throws xBaseJException
*
undefined field type
*/
public char getType() throws xBaseJException
{
if (true)
throw new xBaseJException("Undefined field");
return '_';
}

Everyclassderivedfromitendsuphavingcodelikethis:
/**
* return the character 'D' indicating a date field
*/
public char getType()
{
return 'D';
}

Field.javanowhasthecodebelow.
/**
* @return char field type
*/
public abstract char getType();

ThisforcesaFieldinstancepointertousethegetType() method providedbythederived


class.It'sawinallaround.

Chapter1Fundamentals

60

Youwillnoticethatwehavemultipledump_records()methods.Allofthemtakingdifferent
parameters.Thisisanexampleofpolymorphism.ActuallyIwasforcedintoitbecauseJavaisn't
politeenoughtoallowdefaultargumentvalues,asdoesC++.Thefirstmethodwilldumpallof
therecordsinthedatabase,thesecondwilldumponlythefirstNrecordsinthedatabase,andthe
lastwilldumprecordsXthroughYfromthedatabase.
Ofcourse,inordertodumptherecords,onehastoknowwhateachcolumnisandhowwide
thecolumnwillbeuponoutput. Thefor loopatlistinglines148through 168takescareof
displayingthecolumnheadings.Thecalculationofxisanattempttocentertheheadingoverthe
datacolumn.
Takealookatlistinglines135through137.WhenyouprograminBASIConrealcomputers
youmakeastringofasinglecharacterbycallingSTRING$(len,ascii_value). Ifyouwantto
createastringof30hyphensyouwouldtypethefollowing:
STRING$( 30%, ASCII("-"))

The30%couldofcoursebeanintegervariablelikeX%.TheStringclassforJavacomesup
shortinthisarea.IntheworldofCprogrammingweusedtodeclareagenericbufferatthestart
ofamodule,thendothethreestepshuffle:
char

work_str[1024];

memset( work_str, '\0', sizeof( work_str));


memset( work_str, '-', l_x);
strcat( output_str, work_str);

Cusednullterminatedstrings.Thefirststepnulledoutthebuffer.Thesecondstepputjust
therightnumberofcharactersintothebufferandthethirdstepaddedthebuffertothedestination
string. Itisn't
thatmuchdifferentthanwhatwewereforcedtodoinJava. Herewehadthe
restrictionthatJavadoesn'tusenullterminatedchararraysforstrings.Theexpedientmethodwas
todynamicallydeclarethechararrayeachpassthroughtheforloop. Thefill()methodofthe
Arraysclassallowsustoseteveryelementtoaspecificvalue.Finallywecanaddthehyphen
stringalongwithsomespacestothestringwhichwillactuallybeprinted.
Listinglines152through206getjustalittleugly.Thecodeisn't
complex,itsimplygotugly
trying to fit. I had to deal with page margin issues when writing this, hence the use of the
conditionaloperator?:. IfyouhavereadtheotherbooksinthisseriesyouwillknowthatIam
definitelyNOTafanofthisoperator.Itmakescodedifficulttoread,especiallyforanovice.Itis
reallyshorthandforif ()thenotherwise. Iftheexpressionintheparenthesisevaluatestotrue,
thenyoureturnthevaluebetweenthe?andthe:,otherwise,youreturnthevaluefollowingthe:.
ItistruethatIcouldhavereplacedeachofthoselineswiththefollowing:

Chapter1Fundamentals

61

if (MAX_NAME_LEN > p.getLength())


curr_width = MAX_NAME_LEN
else
curr_width = p.getlength();

Noneofyoureallywantedmetouseif statementsinsideofswitchcases,thoughifyou
workintherealworldyouwillseethemquiteoften.Asageneralrule,youarenotintrouble
untilyournestinggetsmorethan3levelsdeep.
The display logicforeachcolumn isn'tcomplex, butmight require abit of explanation.
MAX_NAME_LENisdefinedtobeelevenbecausetenusedtobethemaximumlengthfora
columnnameundermostxBASEflavors,andelevenensureswewillhaveatleastonetrailing
space.Whendisplayingadatacolumn,Iwantthisexampletobeatleastwideenoughtodisplay
thename. (Onethingwhichreallyannoysmeaboutmostspreadsheetapplicationsistheysize
columnstothedatasize,evenwhenthecolumnisaBoolean.) Whenwearedealingwitha
characterfieldIusethe intheformatstringtoleftjustifytheoutput.MosteverythingelseI
simplyletslamagainsttheright.Idon't
botherformattingdatevaluesinthisexample.Youcan
writethousandsoflinesofcodeformattingdatesineveryformatimaginable,andstillsomebody
willwantthedateformatteddifferently.
testShowMe.java
1) public class testShowMe {
2)
3)
public static void main(String args[]){
4)
showMe s = new showMe();
5)
6)
s.showDBF("class.dbf");
7)
System.out.println( "\n");
8)
s.showDBF( "teacher.dbf");
9)
System.out.println( "\n");
10)
System.out.println( "
Entire class.dbf");
11)
s.dump_records( "class.dbf");
12)
System.out.println( "\n");
13)
System.out.println( "
Records 2 and 3 from class.dbf");
14)
s.dump_records( "class.dbf", 2, 3);
15)
System.out.println( "\n");
16)
System.out.println( "
First record from teacher.dbf");
17)
s.dump_records( "teacher.dbf", 1);
18)
19)
} // end main method
20)
21) } // end class testShowMe

ThetestmodulesimplydisplaysvariousthingsfromthetwoDBFfilescreatedbythesample
programswhicharepostedonthexBaseJWebsite.Ihaveshownyoutheprogramwhichcreates
theclass.dbffileinthisbook.Wedon't
reallyhaveanyneedtocovertheteacher.dbf,butitis
createdbyexample3.javafoundinthexBaseJdistributionorontheSourceForgesite.

Chapter1Fundamentals

62

roland@logikaldesktop:~/fuelsurcharge2$ source ./env1


roland@logikaldesktop:~/fuelsurcharge2$ javac showMe.java
jroland@logikaldesktop:~/fuelsurcharge2$ javac testShowMe.java
roland@logikaldesktop:~/fuelsurcharge2$ java testShowMe
class.dbf has:
3 records
7 fields
FIELDS
Name
Type Length Decimals
--------------------------------------------------CLASSID
C
9
0
CLASSNAME
C
25
0
TEACHERID
C
9
0
DAYSMEET
C
7
0
TIMEMEET
C
4
0
CREDITS
N
2
0
UNDERGRAD
L
1
0
teacher.dbf has:
3 records
4 fields
FIELDS
Name
Type Length Decimals
--------------------------------------------------TEACHERID
C
9
0
TEACHERNM
C
25
0
DEPT
C
4
0
TENURE
L
1
0

Entire class.dbf
CLASSID
CLASSNAME
----------- ------------------------JAVA501
JAVA And Abstract Algebra
JAVA10200
Intermediate JAVA
JAVA10100
Introduction to JAVA

TEACHERID
----------120120120
300020000
120120120

DAYSMEET
----------NNYNYNN
NYNYNYN
NYNYNYN

TIMEMEET
----------0930
0930
0800

CREDITS
----------6
3
3

UNDERGRAD
----------F
T
T

Records
CLASSID
----------JAVA10200
JAVA10100

2 and 3 from class.dbf


CLASSNAME
------------------------Intermediate JAVA
Introduction to JAVA

TEACHERID
----------300020000
120120120

DAYSMEET
----------NYNYNYN
NYNYNYN

TIMEMEET
----------0930
0800

CREDITS
----------3
3

UNDERGRAD
----------T
T

First record from teacher.dbf


TEACHERID
TEACHERNM
----------- ------------------------120120120
Joanna Coffee

DEPT
----------0800

TENURE
----------T

WhenIpastetheoutputintothisbooklayout,weendupwithsomewrappingproblemsdue
tothewidthrestrictionsofthepage.Ihadtoshrinkthefontsoitwouldfitonalineforyou.As
youcansee,theoutputisnicelyformatted.OnceIgetpastdisplayingallofthecolumnsoneach
database,Idisplaytheentirecontentsoftheclass.dbffile.Thisfirstdisplayallowsustoverify
thattheseconddisplay,restrictingoutputtothesecondandthirdrecord,actuallyworked. The
lasttestissimplydisplayingonlythefirstrecordfromtheteacher.dbffile.

Chapter1Fundamentals

63

YouwillsquirrelawaytheshowMe.javasourcefileinyourtoolbox.Youmightevenjointhe
xBaseJprojectteamandfixafewthingswiththelibrary,thencleanthisexampleup.Ifyouwere
payingattentionreadingthechartstartingonpage20youalsonotedthatthexBASEuniverse
containsalotofdatatypeswhichsimplyaren'tsupportedbythislibrary. Autoincrement(+)
wouldbeaniceaddition,butprobablynotthefirstyoushouldtacklesincedoingsowouldmost
likelyrequirethatyouunderstandmoreabouttheheaderrecord.TheDatetime(T)columntype
wouldbeawelcomeaddition.Whenyougetintomoretransactionorientedapplicationsthefield
becomesextremelyimportant. Double(O)andInteger(I)wouldbeinterestingforthosewho
believetheyareUbergeeksandcapableof gettingthosedatatypestobecorrectlystored no
matterwhatplatformxBaseJisrunningon.
ImustwarnyouthatInevertestedthePicture(P)datatype. Ihadnodesiretocreatea
databaseandstoreimagesinit.Moreimportantly,Ihadnodesiretofigureoutwhatimagetypes
(JPEG,BMP,PNG,etc.)wereactuallysupported.Iknowwhypictureswereadded,butInever
playedmuchinthatworld.Imageswereaddedsostorecatalog/inventoryrecordscouldcontaina
memofieldwiththeitemdescription,andapicturefieldwithanimageoftheitem.Ifyouhave
aneBaystorewitharound100items,thisisfine.Ifyouarerunningaseriousbusinessorthinkit
mightturnintoaseriousbusiness,youshouldreallystartwitharelationaldatabaseandbolton
whatyouneedlater.(Rememberthat2GBdatafilelimit?Wealwayssaythe4GBfilelimitwas
imposedbyFAT32,buthaveyoucheckedtheheaderfileand10digittagalgorithmofamemo
filetoensureitisn'tlimitedbyanunsigned32bitintegeraswell?)
1.10
1.10ProgrammingAssignment3
Modifythelasttwodump_records()callsintestShowMe.javatodumponlythesecondrecord
ofclass.dbfandonlythethirdrecordrespectively.Thisisaverysimpleassignmentdesignedto
buildconfidenceintheboundarylogicofthedump_records()method.
CreateyourownversionoftestShowMe.javawhichoperatesontheteacher.dbffile.Ihaven't
providedyouthesourcetocreatetheteacher.dbffile,soyouwillneedtopullitdownfromthe
xBaseJprojectsite.

Chapter1Fundamentals

64
1.11
1.11DescendingIndexesandIndexLifespan

Youhavealreadyseenhowindexescanbeusefulwhenitcomestokeepingdatainasorted
order.Evenifthedataisn't
physicallysorted,theindexallowsyoutoretrieveitintheorderyou
want. Wehaven't
donemuchwithdirectrecordaccessyet,butyouprobablyunderstanditis
anotherbenefitofhavinganindex.
Oneextremelyusefultypeofindexisthedescendingindex.Youwillfindthistypeofindex
usedformanydifferentthingsinaproductionworld.Someinventorysystemsuseadescending
indexfor product keysintheirinventory files. Let's
say yourun agasstationwitha small
convenience storeinit. Youaddnewproductstoyourinventoryfilebasedona4character
productcategoryanda10digititemnumber,thenyouassignitsomekindofbarcodescanvalue.
Youneedtokeepthecategoriessplitoutforvarioustaxreasons.Aslongastheitemnumberis
unique,youdon'tpersonallycarewhatitis.Youmighthavesomedatalookinglikethis:
TOBA0009876543
TOBA0009876542
TOBA0009876541
DARY0000056432
DARY0000056431
DARY0000056430

CIG GENERIC MENTH BOX


CIG GENERIC MENTH
CIG GENERIC
QUART MILK 2%
QUART MILK WHOLE
QUART MILK CHOC 2%

Wheneveryouaddedanewproduct,youwouldonlyneedtoknowwhatcategorytoputit
underandyoursystemcouldautomaticallycalculatethenextitemnumberbyusingthecategory
against the descending key. Given the data above, the first hit for TOBA would return
TOBA00009876543 whichwouldletourroutineaddonetothenumericportionforanewkey.
Likewise,DARY wouldreturnDARY0000056432 . (Yes,daryisreallyspelleddairy,butnot
inoldschoolinventoryspeak.)
xBaseJdidn'tprovideuswith descending indexes. Thiswasactuallyaflawmany early
xBASEpackageshad.Alotofuselessdataendedupgettingstoredinproductiondatafilestrying
to work around this problem. It was not uncommon to find bogus numeric columns which
containedtheresultofafieldofall9's
withanothercolumn(usuallyadate)subtractedfromit.It
wouldbethisboguscolumn,nottheactualcolumn,whichwouldbemadepartofakey.
MYDT
19990801
19550401
19200101

MYDTDS
80009198
80449598
80799898

Asyoucansee,thenewestdatehasthesmallestvalue,whichmakesitthefirstkeyinan
ascending key list. Developers dealing with character fields wrotefunctions and subroutines
whichwouldsubtractthestringfromastringofallZ'stoachievethissamesortorder.

Chapter1Fundamentals

65

Ifyouaresomeonewhohasneverhadamachineslowerthan2Ghzorlessthan2GBof
RAM,youprobablyhavealotoftroubleunderstandingwhydescendingindexesaresoimportant.
YouwillhappilyusethestartBottom()andreadPrev()methodsprovidedbyxBaseJperforming
needlessI/Oonnearlyonethirdofthedatabaselookingforthatoneparticularrecord.Thoseof
uswhogrewupinthePCeraunderstandtheneedcompletely.Weusedtohavetowaitmultiple
secondsforeachrecordtoberetrievedfromthat10MBfullheightharddrive.Eveniftheydeny
it,weallknowthatSeagateaddedthatchirpingcricketsoundandflashinglightsimplytokeep
peopleentertainedwhiletheyweredesperatelytryingtofindthediskblocktheywereinterested
in.
Whileyoucanfindtherecordyouarelookingfor by bruteforce,indexsearchingismuch
lessresourceintensive.I'm
notgoingtoprintthesourceforfind_entry(NodeKey,Node,int)of
theNDX.javafileinthisbook.It's
someprettyintensecode.It's
notdifficulttoread,justoneof
thoseroutineswhereyouhavetowrapyourmindarounditatonesitting,andnotgetuptogoto
thebathroomuntilyouhavefoundwhatyouintendedtofind. Allyouneedtoknowisthatit
reliesonabunchofotherclassestowalkthenodesintheBtreelookingforyourkeyvalue.
Ultimately,itisthismethodwhichgetscalledfromtheDBFclasswheneveryoucallfind( abc ).
(Assuming,ofcourse,thatyouareusingNDXinsteadofMDXasyourindexedfile.)
doeHistory.java
1)
2)
3)
4)
5)
6)
7)
8)
9)
10)
11)
12)
13)
14)
15)
16)
17)
18)
19)
20)
21)
22)
23)
24)
25)
26)
27)
28)
29)
30)

import
import
import
import
import
import

java.io.*;
java.util.*;
org.xBaseJ.*;
org.xBaseJ.fields.*;
org.xBaseJ.Util.*;
org.xBaseJ.indexes.NDX;

public class doeHistory {


// variables used by the class
//
private DBF aDB = null;
// fields
public DateField effectiveDT = null;
private NumField effectiveDTDesc = null;
public NumField fuelPrice = null;
// file names
public final String DEFAULT_DB_NAME = "doehst.dbf";
public final String DEFAULT_K0_NAME = "doe_k0.ndx";
public final String DEFAULT_K1_NAME = "doe_k1.ndx";
// work variables
private boolean continue_flg = true;
private boolean dbOpen
= false;
// result codes
public static final int DOE_SUCCESS
public static final int DOE_DUPE_KEY

= 1;
= 2;

Chapter1Fundamentals

66
31)
32)
33)
34)
35)
36)
37)
38)
39)
40)
41)
42)
43)
44)
45)
46)
47)
48)
49)
50)
51)
52)
53)
54)
55)
56)
57)
58)
59)
60)
61)
62)
63)
64)
65)
66)
67)
68)
69)
70)
71)
72)
73)
74)
75)
76)
77)
78)
79)
80)
81)
82)
83)
84)
85)
86)
87)
88)
89)
90)
91)
92)
93)

public
public
public
public
public
public
public
public
public

static
static
static
static
static
static
static
static
static

final
final
final
final
final
final
final
final
final

int
int
int
int
int
int
int
int
int

DOE_KEY_NOT_FOUND
DOE_FILE_OPEN_ERR
DOE_DEVICE_FULL
DOE_NO_CURRENT_REC
DOE_DELETE_FAIL
DOE_GOTO_FAIL
DOE_DB_CREATE_FAIL
DOE_INVALID_DATA
DOE_END_OF_FILE

=
=
=
=
=
=
=
=
=

3;
4;
5;
6;
7;
8;
9;
10;
11;

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
Method to add a record
//
This method assumes you have values already loaded
//
//
Many different flavors exist to accommodate what the
//
user may have for input
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public int add_record() {
int ret_val = DOE_SUCCESS;
long x;
try {
x = 99999999 - Long.parseLong(effectiveDT.get());
} catch (NumberFormatException n) {
x = 99999999;
} // end catch NumberFormatException
//
// stop the user from doing something stupid
//
if (!dbOpen)
return DOE_FILE_OPEN_ERR;
try {
effectiveDTDesc.put( x);
aDB.write();
} catch ( xBaseJException j){
ret_val = DOE_DUPE_KEY;
System.out.println( j.getMessage());
} // end catch
catch( IOException i){
ret_val = DOE_DEVICE_FULL;
} // end catch IOException

return ret_val;
// end add_record method

public int add_record( String d, String f) {


int ret_val = DOE_SUCCESS;
try {
effectiveDT.put( d);
fuelPrice.put( f);
} catch ( xBaseJException j){
if (j.getMessage().indexOf( "Invalid length for date Field") > -1)
ret_val = DOE_INVALID_DATA;
else
ret_val = DOE_DUPE_KEY;
} // end catch
if (ret_val == DOE_SUCCESS)

Chapter1Fundamentals
94)
95)
96)
97)
98)
99)
100)
101)
102)
103)
104)
105)
106)
107)
108)
109)
110)
111)
112)
113)
114)
115)
116)
117)
118)
119)
120)
121)
122)
123)
124)
125)
126)
127)
128)
129)
130)
131)
132)
133)
134)
135)
136)
137)
138)
139)
140)
141)
142)
143)
144)
145)
146)
147)
148)
149)
150)
151)
152)
153)
154)
155)
156)

return add_record();
else
return ret_val;

public int add_record( Date d, float f) {


int ret_val = DOE_SUCCESS;
try {
effectiveDT.put( d);
fuelPrice.put( f);
} catch ( xBaseJException j){
ret_val = DOE_DUPE_KEY;
} // end catch

if (ret_val == DOE_SUCCESS)
return add_record();
else
return ret_val;

public int add_record( DateField d, NumField f) {


effectiveDT = d;
fuelPrice = f;
return add_record();
}
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
Method to populate known class level field objects.
//
This was split out into its own method so it could be used
//
by either the open or the create.
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
private void attach_fields( boolean created_flg) {
try {
if ( created_flg) {
//Create the fields
effectiveDT
= new DateField( "ef_dt");
fuelPrice
= new NumField( "fuelprice", 6, 3);
effectiveDTDesc = new NumField( "ef_dtds", 8, 0);
//Add field definitions to database
aDB.addField(effectiveDT);
aDB.addField(effectiveDTDesc);
aDB.addField(fuelPrice);
} else {
effectiveDT
= (DateField) aDB.getField("ef_dt");
fuelPrice
= (NumField) aDB.getField("fuelprice");
effectiveDTDesc = (NumField) aDB.getField("ef_dtds");
}

} catch ( xBaseJException j){


j.printStackTrace();
} // end catch
catch( IOException i){
i.printStackTrace();
} // end catch IOException
// end attach_fields method

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
Method to close the database.
//
Don't print stack traces here. If close fails it is
//
most likely because the database was never opened.

67

Chapter1Fundamentals

68
157)
158)
159)
160)
161)
162)
163)
164)
165)
166)
167)
168)
169)
170)
171)
172)
173)
174)
175)
176)
177)
178)
179)
180)
181)
182)
183)
184)
185)
186)
187)
188)
189)
190)
191)
192)
193)
194)
195)
196)
197)
198)
199)
200)
201)
202)
203)
204)
205)
206)
207)
208)
209)
210)
211)
212)
213)
214)
215)
216)
217)
218)
219)

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public void close_database() {
if (!dbOpen)
return;
try {
if (aDB != null) {
aDB.close();
dbOpen = false;
}
} catch (IOException i) {}
}

// end close_database method

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
Method to create a shiny new database
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public void create_database() {
try {
//Create a new dbf file
aDB=new DBF(DEFAULT_DB_NAME,true);
attach_fields(true);

aDB.createIndex(DEFAULT_K1_NAME,"ef_dtds", true, true);


aDB.createIndex(DEFAULT_K0_NAME,"ef_dt",true,true);
dbOpen = true;
} catch( xBaseJException j){
System.out.println( "xBaseJ Error creating database");
j.printStackTrace();
continue_flg = false;
} // end catch
catch( IOException i){
System.out.println( "IO Error creating database");
i.printStackTrace();
continue_flg = false;
} // end catch IOException
// end create_database method

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
Method to delete a record from the database
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public int delete_record() {
int ret_val = DOE_SUCCESS;
if (!dbOpen)
return DOE_FILE_OPEN_ERR;
if (aDB.getCurrentRecordNumber() < 1) {
System.out.println( "current record number " +
aDB.getCurrentRecordNumber());
ret_val = DOE_NO_CURRENT_REC;
}
else {
try {
aDB.delete();
} catch( xBaseJException j){
ret_val = DOE_DELETE_FAIL;
} // end catch
catch( IOException i){
ret_val = DOE_DELETE_FAIL;
} // end catch IOException
} // end test for current record

Chapter1Fundamentals
220)
221)
222)
223)
224)
225)
226)
227)
228)
229)
230)
231)
232)
233)
234)
235)
236)
237)
238)
239)
240)
241)
242)
243)
244)
245)
246)
247)
248)
249)
250)
251)
252)
253)
254)
255)
256)
257)
258)
259)
260)
261)
262)
263)
264)
265)
266)
267)
268)
269)
270)
271)
272)
273)
274)
275)
276)
277)
278)
279)
280)
281)
282)

69

return ret_val;
// end delete_record method

public int delete_record( String d) {


int ret_val = DOE_SUCCESS;
ret_val = find_EQ_record( d);
if ( ret_val == DOE_SUCCESS)
ret_val = delete_record();
return ret_val;
}

// end delete_record method

public int delete_record( int record_num) {


int ret_val = DOE_SUCCESS;
if (!dbOpen)
return DOE_FILE_OPEN_ERR;
try {

aDB.gotoRecord( record_num);
} catch( xBaseJException j){
j.printStackTrace();
ret_val = DOE_NO_CURRENT_REC;
} // end catch
catch( IOException i){
i.printStackTrace();
ret_val = DOE_NO_CURRENT_REC;
} // end catch IOException
if (ret_val == DOE_SUCCESS)
ret_val = delete_record();

return ret_val;
// end delete_record method

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
// Method to dump first 10 records
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public void dump_first_10() {
if (!dbOpen) {
System.out.println( "Must open database first");
return;
} // end test for open database
try {
System.out.println( "\nDate
Price");
System.out.println( "-------- ----------");
for (int x=1; x < 11; x++) {
aDB.gotoRecord(x);
System.out.println( effectiveDT.get() + "
} // end for x loop
} catch( xBaseJException j){
j.printStackTrace();
} // end catch
catch( IOException i){
i.printStackTrace();
} // end catch IOException
}

// end dump_first_10 method

public void dump_first_10_k0() {


if (!dbOpen) {

" + fuelPrice.get());

Chapter1Fundamentals

70
283)
284)
285)
286)
287)
288)
289)
290)
291)
292)
293)
294)
295)
296)
297)
298)
299)
300)
301)
302)
303)
304)
305)
306)
307)
308)
309)
310)
311)
312)
313)
314)
315)
316)
317)
318)
319)
320)
321)
322)
323)
324)
325)
326)
327)
328)
329)
330)
331)
332)
333)
334)
335)
336)
337)
338)
339)
340)
341)
342)
343)
344)

System.out.println( "Must open database first");


return;
// end test for open database

try {
aDB.useIndex( DEFAULT_K0_NAME);
aDB.startTop();
System.out.println( "\nDate
Price");
System.out.println( "-------- ----------");
for (int x=1; x < 11; x++) {
aDB.findNext();
System.out.println( effectiveDT.get() + "
} // end for x loop
} catch( xBaseJException j){
j.printStackTrace();
} // end catch
catch( IOException i){
i.printStackTrace();
} // end catch IOException
}

" + fuelPrice.get());

// end dump_first_10_k0 method

public void dump_first_10_k1() {


if (!dbOpen) {
System.out.println( "Must open database first");
return;
} // end test for open database
try {
aDB.useIndex( DEFAULT_K1_NAME);
aDB.startTop();
System.out.println( "\nDate
Price");
System.out.println( "-------- ----------");
for (int x=1; x < 11; x++) {
aDB.findNext();
System.out.println( effectiveDT.get() + "
} // end for x loop
} catch( xBaseJException j){
j.printStackTrace();
} // end catch
catch( IOException i){
i.printStackTrace();
} // end catch IOException
}

// end dump_first_10_k1 method

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
// Method to find a record
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public int find_EQ_record( String d) {
int ret_val = DOE_SUCCESS;
boolean perfect_hit;
if (!dbOpen)
return DOE_FILE_OPEN_ERR;
try {
aDB.useIndex( DEFAULT_K0_NAME);
perfect_hit = aDB.findExact( d);
if ( !perfect_hit) {
System.out.println( "missed");
System.out.println( "Current Record " +
aDB.getCurrentRecordNumber());

" + fuelPrice.get());

Chapter1Fundamentals
345)
346)
347)
348)
349)
350)
351)
352)
353)
354)
355)
356)
357)
358)
359)
360)
361)
362)
363)
364)
365)
366)
367)
368)
369)
370)
371)
372)
373)
374)
375)
376)
377)
378)
379)
380)
381)
382)
383)
384)
385)
386)
387)
388)
389)
390)
391)
392)
393)
394)
395)
396)
397)
398)
399)
400)
401)
402)
403)
404)
405)
406)
407)

ret_val = DOE_KEY_NOT_FOUND;
}
} catch( xBaseJException j){
System.out.println( j.getMessage());
ret_val = DOE_KEY_NOT_FOUND;
} // end catch
catch( IOException i){
ret_val = DOE_KEY_NOT_FOUND;
} // end catch IOException
}

return ret_val;
// end find_EQ_record method

public int find_GE_record( String d) {


int ret_val = DOE_SUCCESS;
if (!dbOpen)
return DOE_FILE_OPEN_ERR;
try {

aDB.useIndex( DEFAULT_K0_NAME);
aDB.find( d);
} catch( xBaseJException j){
ret_val = DOE_KEY_NOT_FOUND;
} // end catch
catch( IOException i){
ret_val = DOE_KEY_NOT_FOUND;
} // end catch IOException

return ret_val;
// end find_GE_record method

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
Method to retrieve the newest record by date
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public int get_newest() {
int ret_val = DOE_SUCCESS;
if (!dbOpen)
return DOE_FILE_OPEN_ERR;
try {

aDB.useIndex( DEFAULT_K1_NAME);
aDB.startTop();
aDB.findNext();
} catch( xBaseJException j){
ret_val = DOE_KEY_NOT_FOUND;
} // end catch
catch( IOException i){
ret_val = DOE_KEY_NOT_FOUND;
} // end catch IOException

return ret_val;
// end get_newest method

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
method to get next record no matter
//
what index is in use.
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public int get_next() {
int ret_val = DOE_SUCCESS;
try {
aDB.findNext();

71

72

Chapter1Fundamentals

408)
} catch( xBaseJException j){
409)
ret_val = DOE_KEY_NOT_FOUND;
410)
} // end catch
411)
catch( IOException i){
412)
ret_val = DOE_KEY_NOT_FOUND;
413)
} // end catch IOException
414)
415)
return ret_val;
416)
417)
418)
} // end get_next method
419)
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
420)
//
method to retrieve the oldest record
421)
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
422)
public int get_oldest() {
423)
int ret_val = DOE_SUCCESS;
424)
if (!dbOpen)
425)
return DOE_FILE_OPEN_ERR;
426)
427)
try {
428)
aDB.useIndex( DEFAULT_K0_NAME);
429)
aDB.startTop();
430)
aDB.findNext();
431)
} catch( xBaseJException j){
432)
ret_val = DOE_KEY_NOT_FOUND;
433)
} // end catch
434)
catch( IOException i){
435)
ret_val = DOE_KEY_NOT_FOUND;
436)
} // end catch IOException
437)
438)
return ret_val;
439)
} // end get_oldest method
440)
441)
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
442)
//
Method to test private flag and see
443)
//
if database has been successfully opened.
444)
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
445)
public boolean isOpen() {
446)
return dbOpen;
447)
} // end ok_to_continue method
448)
449)
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
450)
//
Method to open an existing database and attach primary key
451)
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
452)
public int open_database() {
453)
int ret_val = DOE_SUCCESS;
454)
455)
try {
456)
457)
//Create a new dbf file
458)
aDB=new DBF(DEFAULT_DB_NAME);
459)
460)
attach_fields( false);
461)
462)
aDB.useIndex( DEFAULT_K0_NAME);
463) //
aDB.useIndex( DEFAULT_K1_NAME);
464)
dbOpen = true;
465)
} catch( xBaseJException j){
466)
continue_flg = false;
467)
} // end catch
468)
catch( IOException i){
469)
continue_flg = false;
470)
} // end catch IOException

Chapter1Fundamentals
471)
472)
473)
474)
475)
476)
477)
478)
479)
480)
481)
482)
483)
484)
485)
486)
487)
488)
489)
490)
491)
492)
493)
494)
495)
496)
497)
498)
499)
500)
501)
502)
503)
504)
505)
506)
507)
508)
509)
510)
511)
512) }

73

if (!continue_flg) {
continue_flg = true;
System.out.println( "Open failed, attempting create");
create_database();
} // end test for open failure

if (isOpen())
return DOE_SUCCESS;
else
return DOE_FILE_OPEN_ERR;
// end open_database method

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
Method to re-index all of the associated index files.
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public void reIndex() {
if (aDB != null) {
if (isOpen()) {
try {
NDX n = null;
for (int i=1; i <= aDB.getIndexCount(); i++) {
n = (NDX) aDB.getIndex( i);
n.reIndex();
}
} catch( xBaseJException j){
j.printStackTrace();
} // end catch
catch( IOException i){
i.printStackTrace();
} // end catch IOException
} // end test for open database
} // end test for initialized database object
} // end reIndex method
//
// We don't need to create a finalize method since
// each object we create has one. You only create a finalize
// method if you allocate some resource which cannot be
// directly freed by the Java VM.
//
// end class doeHistory

Itookthisexamplealittlefartherthanmostofyouprobablywouldinreallife.Thoseofyou
whohavereadotherbooksinthisseriesknowthatatonepointinmylifeIworkedataDEC
VARwhichsoldacustomizedERPpackage.ThepackagewaswritteninDECBASIC.Allof
ourmainfileshadI/Oroutineswrittenforthem.Therewereoneortwoincludefilesyouadded
tothetopofyourprogram,andpresto,youhadaccesstoalloftheI/Oroutinesforthatfile.The
filenameandchannelnumberwaspreassigned,methodswerethereforreading/writing/deleting/
finding records, and all exceptions were caught in the routine itself. Thefunctionsreturned
valueswhichhadstandardizednames;infact,allofthefunction/methodnameswerethesamefor
allofthefiles.

74

Chapter1Fundamentals

WritingthatfirstI/Omodulewasamassivepain.Everyotherfileweaddedafterthatone
wasalmostfree.Dependinguponhowfastatypistyouwere,ittook14hourstocreateallofthe
recordlayoutsandcloneanewI/Osourcefilefromtheoriginal.Theprogramsthemselvesgota
lotsimpler.
MostofyouwillnotethatIdidn't
takethisI/Oroutinequitethatfar.Ididn't
wanttoprinta
5000linesourcefileinthisbook.Evena500linesourcefileispushingitifIactuallywantyou
toreadit.
Itoldyoutheabovestorysoyouwouldhavesomeframeofreferenceforwhythissourcefile
wasstructuredthewayitwas. Javadoesn't
haverecordlayoutsandmaps,soImadethetwo
primaryfieldspublic.IdeliberatelymadeeffectiveDTDescprivatebecausethatisreallyforclass
useonly. IkepttheDBFprivateaswell. AsweproceedwithourdesignImayregretthat
decision,butIwantedtofendoffbit twiddler's
impulse. OddsaregreaterthatIwilladdthe
functiontotheclassifIhavetoopentheclasssourcefiletogetaccesstothedatabaseobject...at
leastthatismyworkingtheory.
Thisclassalsohasabunchofpublicconstants.Allofthefilenamesarestringconstantsand
theresultcodesofintegerconstants.IfyouspendanytimeatallworkinginITandworkingwith
indexedfiles,youwillseeK0usedtorefertoaprimarykey,K1tothefirstalternatekey,etc.I
haveusedthisnotationthroughoutmycareerandthisbookseries. Whenyouaredealingwith
segmentedkeysyouwillseeeachfieldofthekeyhavea.n afterthekeynumber,suchasK1.1,
K1.2,K1.3etc.Thereisneverazerosegment. It's
notbecausewedon't
likezero,wesimply
don't
wanttoconfusemanagementtellingthemK0onthisfileisasinglefieldprimarykey,but
K0.0onthisotherfileindicatesthefirstfieldofasegmentedprimarykey. Asageneralrule,
MBAsdon'tdozerowell.
SinceIdidnotwantsomeuserdirectlymanipulatingtheBooleanworkvariablestheclass
neededImadethemprivate. Oneofthemisforinternaluseonlyandtheotherhasamethod
whichwillbepresentedlatertoallowreadaccess.
Ididquiteabitofpolymorphismwiththisclass.Theadd_record()methodsshouldserveasa
goodexample.TheonewhichdoesactualIOisalsothedefaultone.NoticehowIsubtractthe
datevaluefrom99999999togetavalueforeffectiveDTDesc.Whileitistruethat99999999isn't
avaliddate,itisalsotruethatastringofall9swilldowhatweneed.Sinceourindexesonly
ascend,weneedavaluethatgetssmallerasthedategetsbigger. Theoverriddenmethodsof
add_record()aresimplytheretoprovideaconvenientwayofaddingarecordinasinglestep.

Chapter1Fundamentals

75

Theattach_fields()methodshouldn't
requiremuchexplanationasyouhavealreadyseenme
createamethodjustlikethis.Itiseasiertohandlethisallinonemethodthanreplicateitinother
methods.
Wecouldhavequiteabitofdiscussionovertheclose_database()method. Notsomuch
concerningthemethoditself,butthefactIdidn't
includeafinalize()methodcallingit.Thereare
multipleschoolsofthoughtonthistopic,andnowisagoodtimetodiscussthem.
Myclassdidn'tallocatean
ysystemresourcesonitsown.Itdidn'topen
anyfiles,allocateany
devices,orphysicallyallocateregionsofmemoryonitsown.Everytimeitdidsuchathingit
usedsomethingelsetodoit. OurclassvariableswereallocatedbytheJavaVirtualMachine
(JVM).Theywereeithernativedatatypesorinstancesofclassesprovidedbyothers.Whenyou
developaclass,youareresponsibleforfreeinganyresourceswhichcannotbefreedbytheJVM
inyourfinalize()method.
WhenanobjectgoesoutofscopeitisflaggedforgarbagecollectioninsideoftheJVM.That
collectionmayoccurinstantaneouslyoritmaytakedaysfortheJVMtogarbagecollectit.The
finalize()methodmustphysicallyfreeanyresources(other thandynamicallyallocatedRAM)
whichmightbeneededsomewhereelse.(Wearenotgoingintoadiscussionoverthedifferences
between dynamically allocated and physically mapped memory as it is a topic for lowlevel
programming,notbusinessapplicationprogramming.)
Theclasswehavepresentedhereusesotherclassestoopenthefiles.Whenyoulookatthe
sourceforDBF.java,youwillseethefollowing:
public void finalize() throws Throwable {
try {
close();
} catch (Exception e) {
;
}
}

Whenyoupokearoundinthatsamesourcefileandfindtheclose()method,youwillseethe
following:

Chapter1Fundamentals

76
public void close() throws IOException {
short i;
if (dbtobj != null)
dbtobj.close();
Index NDXes;
NDX n;
if (jNDXes != null) {
for (i = 1; i <= jNDXes.size(); i++) {
NDXes = (Index) jNDXes.elementAt(i - 1);
if (NDXes instanceof NDX) {
n = (NDX) NDXes;
n.close();
}
}
} // end test for null jNDXes 20091010_rth
if (MDXfile != null)
MDXfile.close();
dbtobj = null;
jNDXes = null;
MDXfile = null;
unlock();
file.close();
}

Asyoushouldbeabletotell,thereisacascadingeffectwhengarbagecollectionstartsto
cleanupourclass.WhenitattemptstoreclaimtheclassvariableaDBitwillbeforcedtocallthe
finalize()methodfortheDBFclass.Thatwillfreeupthedatafileandtheindexobjects.The
indexobjectswillthenbegarbagecollected,whichwillfreeupthefilestheyused,etc.
Sincewearetalkingaboutdeletingthings,weshouldmoveontothedelete_record()method
and its overrides. I seriously debated making the default method, which deletes the current
record,private.It's
adangerousthing,especiallyifsomeonedoesn't
bothertochecktheresultsof
their find callbefore performingthedelete. Theoverriddenversions ofthemethod actually
ensuretheylocatethecorrectrecordpriortodeletion.
Iprobablyshouldnothavestuckthedumpmethodsinthisclass,butitmadethemquickto
write.Besides,doingsoensuresIhaveaprogrammingassignmentforyouafterthis.Noticethat
each dump routine makes certain the database is open before proceeding. A lot of Java
developersdon't
dostufflikethis;theysimplyletthingscrash,throwingexceptions.Onething
youmaynotreadilynoticeisthateachdumproutinewhichneedsanindexmakescertainthe
indexitneedsiscurrentlyontopbycallinguseIndex().Readersofotherbooksinthisserieswill
recognizethatasestablishingthekeyofreferencefromotherlanguages.

Chapter1Fundamentals

77

Oncewehaveourkeyofreferenceestablished,theDBFclassprovidestwohandyindex
positioningmethods:startTop()andstartBottom().Onceyouhaveestablishedapositionanda
keyofreferenceyoucanuseeitherfindNext()orfindPrev()tonavigateforwardorbackward
fromyourcurrentposition.TheDBFclassalsoprovidesread()andreadPrev().Thereisabig
differencebetweenthereadandthefindmethods,though. Thereadmethodsrequirethatyou
haveacurrentrecordtoestablishapositioninthefile. Thefindmethodsonlyrequiresome
position tohavebeenestablishedin thecurrently active index. Ifyou open adatabase,call
startTop(),thenattempttocallread()thecallwillfail.
Iaddedtwofindmethodstothisclass.Bothofthemonlyworkwiththeprimarykey.The
firstwillsucceedonlyifaperfectlymatchingkeyisfound;thesecondwillsucceedifakeywhich
isgreaterthanorequaltothesearchvalueisfound. Itshouldbenotedthatthesearehighly
restrictivemethodswhichshouldhavenameswhichbetterindicatetheirrestrictiveness.
TheonlymethodIprovidedtooperateonthesecondindexisget_newest().TheapplicationI
needtowriteinreallifeneedstoquicklyidentifythenewestrecordonfile. Thatrecordwill
providethecurren t valueuntilanewerrecordisadded.Thismethodlooksmuchlikeourother
findmethods. Weestablishakeyofreference,callstartTop()toestablishaposition,thencall
findNext()topullintherecord.
Youmightthinkatfirstglancethatget_next()iscodedincorrectly.Itmakesnoattemptto
establishanykeyofreferenceorpositionviathatkey.Itcouldn't
carelesswhereitisorwhereit
isgoing.Ifyouhappentohitoneendortheotheronthefileitwillletyouknowbyreturning
DOE_KEY_NOT_FOUND,otherwiseitreturnsDOE_SUCCESSandyoucanbefairlycertain
yougottherecordyouwanted.
Ifyouwereconfusedbyget_next()orget_newest(),thenyoushouldloveget_oldest().It's
notacomplexroutine;itsimplyneedsyoutoreallyunderstandbothhowtheindexesworkand
whatthekeyactuallycontains.Remember,indexesarestoredonlyinascendingorderwiththis
library.Thesmallestdatevalueonfilewillbethefirstrecordintheprimaryindex.Wefindthe
oldestrecordbygettingtherecordatthetopoftheprimaryindex(K0)andthenewestrecordby
gettingtherecordatthetopofthesecondary(K1)index.Itistruethatyoucouldalsogetthe
newestrecordonfilebycallingstartBottom()ontheprimarykeyandworkyourwaybacktothe
oldestrecordbyusingfindPrev(), butwhenyougetyourprogramming assignmentsyouwill
thankme.

Chapter1Fundamentals

78

Finallywegettolistingline445.Ihadtokeeptrackofthedatabaseopenstateandprovidea
methodofdeterminingitscurrentstatetotheoutsideworld.ImustapologizetotheJavahackers
oftheworldwhothinkitisjustdandytoneverprovidethiscapabilityandtojustletthingsthrow
exceptionsinproduction.Iamnotfromthatschool.I'm
fromtheschoolofthosewhousedto
workinoperationsandhadtowakepeopleupat2AM.I'm
alsofromtheschoolofthosewho
usedtoworkproductionsupportalongwiththeirdevelopmentdutiesandhadtogetthosecallsat
2AM.Thereasonweusedtomakeprogrammersstartoutinoperations,thenworkproduction
support,istoteachthemwhathappenswhenbustedthingsgetsturnedintoproduction. Call
isOpen()beforeyoudosomethingandavoidcrashingfromanunhandledexception.
WewilltalkaboutreIndex()afterwediscussthetestapplication.
testDoeHistory.java
1)
2)
3)
4)
5)
6)
7)
8)
9)
10)
11)
12)
13)
14)
15)
16)
17)
18)
19)
20)
21)
22)
23)
24)
25)
26)
27)
28)
29)
30)
31)
32)
33)
34)
35)
36)
37)
38)
39)
40)
41)
42)

import java.text.*;
import java.util.*;
import java.io.*;
import org.xBaseJ.*;
public class testDoeHistory {
public static void main(String args[]){
doeHistory d = new doeHistory();
//
// You must set this unless you want NULL bytes padding out
// character fields.
//
try {
Util.setxBaseJProperty("fieldFilledWithSpaces","true");
} catch (IOException e) {
System.out.println( "An IO Exception occurred");
System.out.println( e.toString());
e.printStackTrace();
}
d.create_database();
if (d.isOpen()) {
String line_in_str = null;
long l_record_count = 0;
boolean eof_flg = false;
FileReader in_file = null;
BufferedReader input_file = null;
try {
in_file = new FileReader( "fuel_prices.csv");
} catch (FileNotFoundException f) {
System.out.println( "File Not Found fuel_prices.csv");
eof_flg = true;
} // end catch for file not found
if (eof_flg == false) {
input_file = new BufferedReader( in_file,4096);
System.out.println("\nPopulating database");
}

Chapter1Fundamentals
43)
44)
45)
46)
47)
48)
49)
50)
51)
52)
53)
54)
55)
56)
57)
58)
59)
60)
61)
62)
63)
64)
65)
66)
67)
68)
69)
70)
71)
72)
73)
74)
75)
76)
77)
78)
79)
80)
81)
82)
83)
84)
85)
86)
87)
88)
89)
90)
91)
92)
93)
94)
95)
96)
97)
98)
99)
100)
101)
102)
103)
104)
105)

while (eof_flg == false) {


try {
line_in_str = input_file.readLine();
}
catch (EOFException e) {
System.out.println( "End of file exception");
eof_flg = true;
}
catch (IOException e) {
System.out.println( "An IO Exception occurred");
System.out.println( e.toString());
e.printStackTrace();
eof_flg = true;
}
if (eof_flg == true)
continue;
if (line_in_str == null) {
System.out.println( "End of intput file reached");
eof_flg = true;
continue;
}
l_record_count++;
String input_flds[] = line_in_str.split( ",");

try {
d.effectiveDT.put( input_flds[0]);
d.fuelPrice.put( input_flds[1]);
d.add_record();
} catch ( xBaseJException j){
j.printStackTrace();
} // end catch
// end while loop to load records

System.out.println( "Finished adding " + l_record_count +


" records\n");
//
// Now that we have some data, let's use some
// of the other methods
//
// First make sure the open works
d.close_database();
doeHistory h = new doeHistory();
h.open_database();
if (!h.isOpen()) {
System.out.println("Unable to open the database");
} else {
// add a record with a future date
//
int x;
x = h.add_record( "20121003", "13.41");
System.out.println( "Result of add " + x);
try {
h.effectiveDT.put( "20110830");
h.fuelPrice.put( "29.95");
} catch( xBaseJException j) { j.printStackTrace();}
x = h.add_record();
System.out.println( "result of second add " + x);
System.out.println( "First 10 in order added");

79

Chapter1Fundamentals

80
106)
107)
108)
109)
110)
111)
112)
113)
114)
115)
116)
117)
118)
119)
120)
121)
122)
123)
124)
125)
126)
127)
128)
129)
130)
131)
132)
133)
134)
135)
136)
137)
138)
139)
140)
141)
142)
143)
144)
145)
146)
147)
148)
149)
150)
151)
152)
153) }

h.dump_first_10();
System.out.println( "First 10 in descending date order");
h.dump_first_10_k1();
System.out.println( "First 10 in ascending date order");
h.dump_first_10_k0();
// Now let us see what keys have actual
// data
//
System.out.println( "\nBefore reIndex\n");
System.out.println( "finding 20071010");
x = h.find_EQ_record( "20071010");
System.out.println( "\nResult of EQ find " + x + "\n");
System.out.println( "Date: " + h.effectiveDT.get()
+ " Price: " + h.fuelPrice.get());
x = h.get_newest();
System.out.println( "Result of get_newest " + x);
System.out.println( "Date was: " + h.effectiveDT.get());
//
Not all keys are updated when using NDX
//
h.reIndex();
System.out.println( "\nAfter reIndex\n");
System.out.println( "First 10 in descending date order\n");
h.dump_first_10_k1();
System.out.println( "\nfinding 20071010");
x = h.find_GE_record( "20071010");
System.out.println( "\nResult of EQ find " + x + "\n");
System.out.println( "Date: " + h.effectiveDT.get()
+ " Price: " + h.fuelPrice.get());
if ( x == h.DOE_SUCCESS) {
x = h.delete_record();
System.out.println( "Result of delete " + x + "\n");
}
x = h.get_newest();
System.out.println( "Result of get_newest " + x);
System.out.println( "Date was: " + h.effectiveDT.get());

}
}

h.close_database();
} // end test for successful open
// end test for open dbf

// end main method

// end class testShowMe

ThisisoneofthelongertestprogramsIhaveprovidedyou.Abigpartofthatisduetothe
factIcreatedaCSV(CommaSeparatedValue)filecalledfuel_prices.csvwhichhaslinesinit
lookinglikethis:

Chapter1Fundamentals

81

20070905,289.3
20070912,292.4
20070919,296.4
20070926,303.2
20071003,304.8
20071010,303.5
20071017,303.9
20071024,309.4
20071031,315.7
20071107,330.3
20071114,342.5
20071121,341.0
20071128,344.4

Actuallyithasover100linesinit,butI'm
certainlynotgoingtoprintithere.Ifyouwant,
youcanvisittheDepartmentofEnergyWebsiteandpulldownthespreadsheetwhichhashistoric
dieselfuelprices,andcreateyourownfile.
IntheoryIcouldhavedonetheUtilcallfoundatlistingline17insideofthedoeHistoryclass,
butIdidn't
haveawarmandfuzzyfeelingabouttheactualruntimescopeofUtilinallsituations.
Feelfreetoexperimentonyourownwithplacingthiscallatvariousplacesintheclasshierarchy.
Listinglines26through78servenootherpurposethantoreadalinefromthisCSVandload
itasarecordinthedatabase. SinceItriedtoimplementlocalizederrorhandlingandprovide
meaningfulerrormessages,thiscodeisalotlargerthanyouwillseeinmostexampleswhich
wouldsimplytrapallexceptionsatoneplaceandprintastacktrace.
Weshoulddiscussthiscodebrieflyforthosewhohavenevertriedtoreadlinesinfromatext
filebefore.FirstyouhavetocreateaFileReaderobjectasIdidatlistingline33.Onceyouhave
donethatyoucancreateaBufferedReaderobjecttoreadfromandbuffertheFileReaderobject
youjustcreatedasIdidatlistingline40.Thesecondparameter(4096)isanoptionalbuffersize
inbytes.Ifyoudonotpassabuffersize,thereissomevaluewhichgetsusedbydefault.
OnehastouseaBufferedReaderobjectifonewishestoreadalineofinputatatimeaswe
doatlistingline46.ThereadLine()methodofaBufferedReaderobjectensuresthatweeitherget
allcharactersasaStringuptothenewLinecharacterortheendofthestream. Youwillnot
receivethenewLineorendofstreamterminationcharacter(s)intheString.
Afterwegetdonedealingwiththepotentialendoffilesituationweincrementtherecord
counterthenusethereallycoolsplit()methodprovidedbytheStringclass.Sinceweknowthe
numberandorderofdataintheinputfile,wecandirectlyputthevaluesintothedatabasefields
andaddtherecordtothedatabase.Roughly50linesofcodejusttogetourtestdata,butnowwe
haveit.

Chapter1Fundamentals

82

Listinglines84through147containthemeatofthistest.Weneedtoseetheoutputbefore
wetalkaboutthem,though.
roland@logikaldesktop:~/fuelsurcharge2$ java testDoeHistory
Populating database
End of input file reached
Finished adding 107 records
Result of add 1
result of second add 1
First 10 in order added
Date
Price
-------- ---------20070905
89.300
20070912
92.400
20070919
96.400
20070926
03.200
20071003
04.800
20071010
03.500
20071017
03.900
20071024
09.400
20071031
15.700
20071107
30.300
First 10 in descending date order
Date
Price
-------- ---------20090916
63.400
20090909
64.700
20090902
67.400
20090826
66.800
20090819
65.200
20090812
62.500
20090805
55.000
20090729
52.800
20090722
49.600
20090715
54.200
First 10 in ascending date order
Date
-------20070905
20070912
20070919
20070926
20071003
20071010
20071017
20071024
20071031
20071107

Price
---------89.300
92.400
96.400
03.200
04.800
03.500
03.900
09.400
15.700
30.300

Before reIndex
finding 20071010
Result of EQ find 1
Date: 20071010 Price: 03.500

Chapter1Fundamentals

83

Result of get_newest 1
Date was: 20090916
After reIndex
First 10 in descending date order
Date
-------20121003
20110830
20090916
20090909
20090902
20090826
20090819
20090812
20090805
20090729

Price
---------13.410
29.950
63.400
64.700
67.400
66.800
65.200
62.500
55.000
52.800

finding 20071010
Result of EQ find 1
Date: 20071010 Price: 03.500
Result of delete 1
Result of get_newest 1
Date was: 20121003

Youshouldnotethattheresultofboththefirstadd(20121003,13.41)andthesecondadd
(20110830,29.95)returneda1,meaningtheyweresuccessfullyaddedtothedatabase,yetthey
didn'tshowuponourinitialdumpreports.Therecordsdon'tshowupuntil
afterIcallreIndex().
Hereisanotherlovelylittletidbitforyou.NDXobjectsdon't
monitorchanges.If,insteadof
obtainingthetheNDXcurrentlyattachedtotheDBFobject,Isimplycreatetwonewobjectsand
reindex,thosechangeswillbereflectedinthefile,butnotinourapplication.
NDX a = new NDX( DEFAULT_K0_NAME, aDB, false);
a.reIndex()
NDX b = new NDX( DEFAULT_K1_NAME, aDB, false);
b.reIndex()

Thecodeabovewillnotplaceentriesinourindexeventhoughthevalueswillbecorrecton
file.Why?BecausetheBtreegetsloadedintoRAM. Youhavetomanipulatetheexactsame
Btreethedatabaseobjectisusing.Makenomistake,acalltoreIndex()changesthecontentsof
thefile,buttheotherloadedviewofitdoesnot. Youshouldnever,underanycircumstances,
attempttoletmultipleusershavewriteaccesstothesameDBFforthis,andmanyother,reasons.
ThereisnotriggeringmethodinplacetokeepBtreesinsynchbecausethereisnodatabaseengine
inplace.

84

Chapter1Fundamentals

Takeanotherlookatlistingline463indoeHistory.java.IhavetheuseIndex()forthesecond
keycommentedout.OnpagetwelveinthisbookItoldyouthatrecordscouldbeaddedtoaDBF
filewithoutevercreatinganentryinanindexfile.Thistesthasbeenashiningexample.When
wecallopen_database()weonlyopenoneindex.Indeed,thedatabaseobjectdoesn't
careifwe
choose to not openany. Agood many xBASElibraries out there support only readingand
writingofrecordsinxBASEformat.Theyprovidenoindexsupportwhatsoever.
1.12
1.12ProgrammingAssignment4
Thisisamultipartassignment. Yourfirstassignmentistouncommentlistingline463,
recompileandrerunthisapplication.Youwillnotethatthefirstsetofdumpreportsnowhasthe
addedrecords.Canyouexplainwhy?
Part2:Moveallofthedump_methodsoutofdoeHistory.javaandmakethemmethodsin
testDoeHistory.java.Getthemtoactuallywork.DONOTMAKETHEDBFOBJECTPUBLIC
ORUSEAMETHODWHICHPROVIDESACOPYOFITTOACALLER. Don't
forgetto
check for the database being open prior to running. Do not add any new methods to
doeHistory.java.
Part3: Aftercompletingpart two,change getNext()touseread() insteadoffindNext(),
compileanddocumentwhat,ifany,differencethereisintheoutput.
Part 4: After completing part three, add one method to doeHistory.java which uses the
readPrev()methodoftheDBFclasstoreadthepreviousrecord.Clonethedump_first_10_k1()
methodyoujustmovedtotestDoeHistory.javatoamethodnameddump_last_10_k0().Without
usingthealternateindex,getthesame10recordstodisplay.

Chapter1Fundamentals

85

1.13
1.13DeletingandPacking
Imentionedmuchofthisinformationearlierbutwearegoingtogooveritagainindetail
becauseittendstocatchmostnewbiesoffguardevenaftertheyhavebeentoldahundredtimes.
DeletingarecordinanxBASEfiledoesnotphysicallydeletetherecord(inmostversions),nor
does it update any NDX index file information. (A production MDX is a slightly different
situation.)
Basically,eachrecordinaDBFfilehasanextrabyteatthefrontofit. Whenthatbyteis
filledin,usuallywithanasterisk,therecordisconsideredtobedeleted . Yourapplicationswill
continuetoreadthisrecordandprocessitunlesstheycallthedeleted()methodimmediatelyafter
readingarecord.Thedeleted()methodoftheDBFclassreturnstrueiftherecordisflaggedfor
deletion.
OneofthedBASEfeaturesgeneralusersfoundmostendearingwastheabilitytound elete
arecordtheyhadaccidentallydeleted.Thisfeaturewaspossiblesimplybecausetherecordhad
notbeenphysicallydeleted.TheDBFclassprovidesanundelete()methodforyouaswell.Ifyou
findarecordwhichhasbeenmarkedasdeletedthatyouwishtorestore,yousimplyreaditand
callundelete().
ItisnotunusualtofindxBASEfileswhichhaveneverhaddeletedrecordsremoved.Aslong
asauserneverhitsthe2GBfilesizelimitforaDBF,thereisnothingwhichforcesthemtogetrid
ofdeletedrecords.Untilyouhitasizelimit(eithermaximumfilesizeorrunoutofdiskspace),
youcanjustgohappilyonyourway.
Whatifyouwanttogetthatspaceback?Whatifyouneedtogetitback?Well,thenyou
needtoknowaboutthepack()methodoftheDBFclass. Manybookswilltellyouthatpack()
removesthedeletedrecordsfromyourdatabase. TheremayactuallybeanxBASEtoolsetout
theresomewherewhichactuallyimplementspack()thatway. AlmosteverylibraryIhaveused
throughoutmycareerdoeswhatxBaseJdoes.Theycreateashinynewdatabasewithatemporary
filename,copyalloftherecordswhicharenotflaggedfordeletiontothetemporarydatabase,
closeandnuketheoriginaldatabase,thenrename/copythetemporarybacktowheretheoriginal
databasewas. Ifyouarelookingtopack() becauseyouareoutofdiskspace,itisprobably
alreadytoolateforyouunlessyour/tmportmpenvironmentvariableispointingtoadifferent
physicaldisk.
CarefulreaderswillnotethatIdidn't
sayanythingaboutyourindexfiles.pack()couldn'tcar
e
lessaboutthem. Ifyoudonotreindexyourindexfilesorcreatenewindexfilesaftercalling
pack(),thenyouareaskingfordisaster.

Chapter1Fundamentals

86
testpackDoeHistory.java
1) ...
2)
3)
4)
5)
6)
7)
8)
9)
10)
11)
12)
13)
14)
15)
16)
17)
18)
19)
20)
21)
22)
23)
24)
25)
26)
27)
28)
29)
30)
31)
32)
33)
34)
35)
36)
37)
38)
39)
40)
41)
42)
43)
44)
45)
46)
47)
48)
49)
50)
51)
52)
53)
54)
55)
56)
57)
58)
59)
60)
61)

System.out.println( "Finished adding " + l_record_count +


" records\n");
//
// Now that we have some data, let's use some
// of the other methods
//
// We need to delete a few records now
for ( int i=1; i < 20; i +=3)
d.delete_record( i);
// First make sure the open works
d.close_database();
// Cheat because I didn't supply the pack method
//
try {
DBF aDB = new DBF(d.DEFAULT_DB_NAME);
System.out.println( "\npacking the database");
aDB.startTop();
aDB.pack();
System.out.print( "\nDatabase has been packed ");
System.out.println( "record count " + aDB.getRecordCount());
aDB.close();
} catch( xBaseJException j) {
j.printStackTrace();
} catch( IOException e) {
e.printStackTrace();
} catch ( CloneNotSupportedException c) {
c.printStackTrace();
}
doeHistory h = new doeHistory();
h.open_database();
if (!h.isOpen()) {
System.out.println("Unable to open the database");
} else {
// add a record with a future date
//
System.out.println( "\nadding records with future dates");
int x;
x = h.add_record( "20121003", "13.41");
try {
h.effectiveDT.put( "20110830");
h.fuelPrice.put( "29.95");
} catch( xBaseJException j) { j.printStackTrace();}
x = h.add_record();
x
x
x
x
x

=
=
=
=
=

h.add_record(
h.add_record(
h.add_record(
h.add_record(
h.add_record(

"20201003",
"20190903",
"20180803",
"20170703",
"20160603",

"19.58");
"21.58");
"19.58");
"21.58");
"19.58");

System.out.println( "First 10 in order added");


h.dump_first_10();
System.out.println( "First 10 in descending date order");

Chapter1Fundamentals
62)
63)
64)
65)
66)
67)
68)
69)
70)
71)
72)
73)
74)
75)
76)
77)
78)
79)
80)
81)
82)
83)
84)
85)
86)
87)
88)
89)
90)
91)
92)
93)
94)
95)
96)
97)
98)
99)
100)
101)
102)
103)
104)
105)

87

h.dump_first_10_k1();
System.out.println( "First 10 in ascending date order");
h.dump_first_10_k0();
// Now let us see what keys have actual
// data
//
System.out.println( "\n\nBefore reIndex\n");
System.out.println( "finding 20071010");
x = h.find_EQ_record( "20071010");
System.out.println( "\nResult of EQ find " + x + "\n");
System.out.println( "Date: " + h.effectiveDT.get()
+ " Price: " + h.fuelPrice.get());
x = h.get_newest();
System.out.println( "Result of get_newest " + x);
System.out.println( "Date was: " + h.effectiveDT.get());
//
Not all keys are updated when using NDX
//
h.reIndex();
System.out.println( "\nAfter reIndex\n");
System.out.println( "First 10 in descending date order\n");
h.dump_first_10_k1();
System.out.println( "\nfinding 20071010");
x = h.find_GE_record( "20071010");
System.out.println( "\nResult of EQ find " + x + "\n");
System.out.println( "Date: " + h.effectiveDT.get()
+ " Price: " + h.fuelPrice.get());
if ( x == h.DOE_SUCCESS) {
x = h.delete_record();
System.out.println( "Result of delete " + x + "\n");
}
x = h.get_newest();
System.out.println( "Result of get_newest " + x);
System.out.println( "Date was: " + h.effectiveDT.get());

}
}

h.close_database();
} // end test for successful open
// end test for open dbf

// end main method

IdidnotprovidethebeginningofthissourcefilebecauseIdidn't
feellikereprintingthe
codetoloadtherecordsagain.Ifyouwanttogetthisprogramrunningyoucansimplystealthe
CSVimportcodefromthepreviouslypresentedprogram.
Atlistingline9Icreatedaforloopwhichwilldeleterecordsfromthedatabase.Weonly
needafewnearthebeginningtodisappear.
Listinglines18through24containthecodewhereIopenthedatabaseandcallpack(). I
cheatedhereanddirectlycreatedadatabaseobjectbecauseIhadnotaddedapack_database()
methodtothedoeHistoryclass.

Chapter1Fundamentals

88

Youwillnoticeatlistinglines53through57thatIchosetoaddsomemorerecords.Ijust
wantedtomakethingspainfullyobviousduringtherestofthetest.Thereisnothingreallymagic
aboutthevaluesinthoserecords,otherthanthefacttheyareeasytospot.
Payspecialattentiontolistingline82.DoyourememberwhatIsaidearlier?Ideliberately
leftthislinewhereitwastoprovethatstatement.Now,let'stakealookattheoutput.
roland@logikaldesktop:~/fuelsurcharge2$ javac doeHistory.java
roland@logikaldesktop:~/fuelsurcharge2$ javac testpackDoeHistory.java
roland@logikaldesktop:~/fuelsurcharge2$ java testpackDoeHistory
Populating database
End of intput file reached
Finished adding 107 records
packing the database
Database has been packed record count 100
adding records with future dates
First 10 in order added
Date
Price
-------- ---------20070912
92.400
20070919
96.400
20071003
04.800
20071010
03.500
20071024
09.400
20071031
15.700
20071114
42.500
20071121
41.000
20071205
41.600
20071212
32.500
First 10 in descending date order
Date
Price
-------- ---------20160603
19.580
20170703
21.580
20180803
19.580
20190903
21.580
20201003
19.580
20110830
29.950
20121003
13.410
20090916
63.400
20090909
64.700
20090902
67.400
First 10 in ascending date order
Date
-------20070912
20070919
20071003
20071010
20071024
20071031
20071114

Price
---------92.400
96.400
04.800
03.500
09.400
15.700
42.500

Chapter1Fundamentals
20071121
20071205
20071212

89

41.000
41.600
32.500

Before reIndex
finding 20071010
Result of EQ find 1
Date: 20071031 Price: 15.700
Result of get_newest 1
Date was: 20160603
After reIndex
First 10 in descending date order
Date
-------20201003
20190903
20180803
20170703
20160603
20121003
20110830
20090916
20090909
20090902

Price
---------19.580
21.580
19.580
21.580
19.580
13.410
29.950
63.400
64.700
67.400

finding 20071010
Result of EQ find 1
Date: 20071010 Price: 03.500
Result of delete 1
Result of get_newest 1
Date was: 20201003

Pleaselookatthehighlightedlinesintheoutput.WhenwearedoneloadingtheCSVfile
intothedatabasethereare107records.Afterdeletingandpackingwehave100records.Takea
lookatthedescendingdateorderreport. Theoutputisquiteobviouslytrashed. Theindexes
haven'tbeen
updated.Theystillhavevalueswhichpointtorecordnumbers.Theonlyproblemis
thatthoserecordsnolongercontaininformationwhichcorrespondstotheindexvalue.
Ineedtopointoutthatitisquitepossibletocrashwhenusingastaleindexfileagainsta
recentlypackeddatabase.Youcouldattempttoreadarecordnumberwhichdoesn't
existinthe
database.Itallreallydependsonwhatkeyyouareusingandhowmanyrecordsweredeleted.
ScandowntothereportgeneratedafterwecalledreIndex().Noticethateverythingisbackto
thewayyouexpectittobe.

90

Chapter1Fundamentals

IfyouusexBASEproductslongenough,youwilleventuallyfindyourselfinasituationin
which youneedtopackadatabase. Packingadatabaseisalwaysagamble. Ifyouareina
productionenvironmentyouwillsimplynotknoweveryindexfileeveryapplicationcreatedto
viewyourdatatheamanneritchose.Youalsowon't
haveanywaytotellthoseapplicationsthey
needtorebuildtheindex.Itistheresponsibilityofthepackertocontactalloftheotherusers,not
justletthemcrash.
1.14
1.14ProgrammingAssignment5
Add a pack_database() method to doeHistory(). Don'tjust call pack(), reindex BOTH
indexes.Youwon't
justbeabletocallreIndex().Ifyoureadthatcodecarefullyyouwillseethat
itreliesonallindexfileshavingbeenopenedandattachedalready.
1.15
1.15DataIntegrity
Wetouchedonthisabitinthelastsection,butIneedtodrivethepointhomenow.Idon't
care what xBASE library you use orwhat language you work in,without a database engine
betweeneveryapplicationandtheactualdata,youcannotandwillnothavedataintegrity.
Dataintegrityismuchmorethansimplykeepingindexesinsynchwithactualdatarecords.
Dataintegrityinvolvesdatarulesandyoucannotimplementruleswithoutanengine,VM,or
someothersingleaccesspointbetweentheapplicationandtheactualdata.
Don't
worry,I'm
notgoingtoboreyouwithahighlytechnicalrantthatsoundslikealecture
onvenerealdisease.Dataintegrityisquiteeasytoexplaininlayman'sterms.
Letussaythatyourunalandfill.Everycompanywhichhassignedacontractwithyouhas
providedalistoftrucknumbersalongwithothertruckidentifyinginformation. Thesearethe
onlytrucksallowedtodumpatyourlandfillbecausethecompanieswillpayfortheirowntrucks
only.YoudutifullyputallofthisinformationintoaDBFfileusingauniquesegmentedkeyof
companyIdandtruckNum.
Withthattaskoutofthewayyousetaboutwritingyourscaleticketapplication.Theinbound
scaleoperatorwillpickatruckfromthelistyouprovideandkeyinthetruckfull(gross)weight.
Thesystemwillfillinthedateandtimebeforewritingtherecordtothetransactionfile.Afterthe
truckisdumped,itwillpullontotheoutboundscalewherethatoperatorwillpullpicktheticket
recordfromthelistofavailableticketsbaseduponthetrucknumberandcompany. Once the
operatorkeysintheempty(tare)weightthesystemwillprinttheticketandthescaleoperatorwill
handacopytothedriver.

Chapter1Fundamentals

91

Haveyounoticedanyproblemsyet?
You,thedeveloper,hadtowriteanapplicationwhichenforceddataintegrityonitsown.You
didn't
letthescaleoperatorkeyintruckinformation,heorshehadtopickfromalist,presumably
alistyoupopulatedfromyourtruckandcompanydatabase.Theoutboundscaleoperatorhadto
chooseoneofthecurrentlyopenticketstocomplete.
Thereisnothingintheenvironmentwhichwouldstopaticketrecordfromgettingwrittento
theticketdatabasewithatruckandcompanythatisn't
onthetruckandcompanydatabase.Your
applicationisprovidingthatdatarule,butthenextprogrammermaynotbeawareoftherule.
Withoutadatabaseengineprovidingasinglepointofaccessforallusersandapplications,
there isnomethodof enforcing integrityrules. There isno requirement that this engine be
relational,itsimplyneedstoprovidecontrolandrestrictaccess.
Itmightseemdifficulttounderstand,buttherearepeopleoutthereintoday's
worldpaying
hundredsandsometimesthousandsofdollarsforcommercialxBASEproductswhichprovidethis
verything.AruntimeorvirtualmachineisinstalledunderadifferentuserIDwhichownsand
controlsalldataandindexfiles.Theruntimecoordinatesallaccesstothedataandinmanycases
willenforcedataintegrityrules.Somewillevenforcetherebuildingofindexfileswhenevera
databasefileispacked.
Wearen't
dealingwithanengineorasinglepointofaccess.Ifyourapplicationisgoingto
haveanyformofdataintegritythenyouaregoingtohavetocodeitin.
1.16
1.16ProgrammingAssignment6
Thisismoreofa learnitonyourown assignment.Pickanyoneofthedatabaseswehave
coveredwhichhasauniqueprimarykeydefined. Writeormodifyaprogramwhichaddsfive
recordstothatfile.Afteraddingfiverecords,makesuretherecordsappearwhenreportingvia
theuniquekey. Oncetheyarethere,deletethreeofthoserecords,thenattempttoaddthree
recordswhichhavethesameprimarykeyvalue.
Whathappens?
Ifyoumanagetogettherecordsadded,whathappenswhenyouattempttoreIndex()?
Howaboutwhenyoutrytoundelete?

92

Chapter1Fundamentals

1.17
1.17Summary
It should now be obvious that xBaseJ provides a lot of functionality for applications of
limitedsizeandscope. Whenallyouneedisanindexedfileofsomekindforastandalone
applicationthiscanbeagreattool. Asyoushouldhavelearned from theindexproblemswe
covered,itisnotformultiuseruse.YouwillfindthatmostxBASElibrariesoutthereonlytalk
aboutbeingmultiuserforthedatafile,nottheindexes.Inordertogainspeed,mostofthese
librariesloadtheindexfilecontentsintomemory.
YoucangetalotofspeedoutofanxBASEfileformatontoday's
computerhardware.Most
of the concepts and original libraries were written to run on dual floppy computers running
4.77Mhz.Laterreimplementationsoftheselibrariesusedlessefficienthigherlevellanguages,
butmosttriedtohonortheoriginalspecifications.Javaisnotalanguageknownforperformance,
butonamachinewitha1Ghzorfasterclockspeed,itreallydoesn'thavetobethatefficient.
Withallthatithasgoing for it,adeveloperhastorememberthatxBASEwasoriginally
createdtosolveadatastorageandorderedinputproblem,notprovidethetypesofdataservices
weassociatewithtoday's
relationaldatabases. Theoriginalcreatorcouldnothaverealistically
envisioned all of the bastardized uses for this solution people would come up with in the
followingdecades.Oddlyenough,itisthehorrorstoriesfromimplementationsthatshouldhave
neverbeensignedoffonwhichgavexBASEitssomewhatmalignedreputation.
Youmightfinditdifficulttobelieve,butnexttoaCSV(CommaSeparatedValue)file,a
DBFfileisoneofthemostcommonmethodsofdataexchange.WhilesomecoreDBFformats
are widely supported, NDX and MDX files are not widely supported. xBaseJ focuses on
supportingtheminimalcoreofDBFdatatypesalongwiththememofields.Idon't
reallyknow
whythePicturedatatypewassupported,unlessthatwassimplyfalloutfromaddingMemofield
support.
Itwouldbeniceifoneormoreofyoureadingthiswouldtakeituponyourselvestoadd
supportforDatetime(T)andAutoincrement(+)sincethosedatatypesbecameratherwidelyused
inlateryears.Ihaven't
researchedeithersubject,butIimaginethatneithertypewillbeeasyto
adduntilsupporthasbeenaddedfornativeInteger(I).
IdidnotcoverMDXsupportatthistimebecauseitdoesn't
work.Keysareadded,butsort
orderisnotmaintained.

Chapter1Fundamentals

93

1.18
1.18ReviewQuestions
1. Whattwosituationsforceauserorapplicationtophysicallyremovedeletedrecords?
2. Bydefault,whatarestringandcharacterfieldspaddedwithwhenusingxBaseJ?
3. IfyouhaveaDBFopenwithNDXfilesattachedtoitthencallasubroutinewhichcreates
newNDXobjectsforthosesamefilesandcallsreIndex()onthem,willthechangestothe
indexfilesbereflectedintheNDXobjectsyouDBFholds?Whyorwhynot?
4. WhattwoJavaclassesdoyouneedtousetobuildcreateareportlinemakingthedataline
upincolumns?
5. HowdoesonetellxBaseJtopadstringandcharacterfieldswithspaces?
6. WhatDBFclassmethodphysicallyremovesrecordsfromthedatabase?
7. WhatisthemaximumsizeofaDBFfile?
8. WhatDBFclassmethodisusedtoretrieveavaluefromadatabaseFieldregardlessof
fieldtype?
9. AftercreatingashinynewDBFobjectandcorrespondingdatafile,whatmethoddoyou
usetoactuallycreatecolumnsinthedatabase?
10. WhatDBFclassmethodisusedtoassignavaluetoadatabaseField?
11. WhatDBFclassmethoddoyoucalltochangetheNDXkeyofreference?
12. WhatDBFclassmethodignoresallindexesandphysicallyreadsaspecificrecord?
13. Whenyoudeleteadatabaserecord,isitactuallydeleted?
14. WhatDBFclassmethodsetsthecurrentrecordtozeroandresetsthecurrentindexpointer
totherootofthecurrentindex?
15. WhatisthemaindifferencebetweenreadNext()andfindNext()?
16. Whatfunctionormethodreturnsthenumberofrecordsonfile?
17. Whathappenswhenyouattempttostoreanumericvaluetoolargeforthecolumn?
18. Whathappenswhenyouattempttostoreacharactervaluetoolargeforthecolumn?
19. Whenaccessingviaanindex,howdoyouobtaintherecordoccurringbeforethecurrent
record?
20. WhatDBFmethodreturnsthenumberoffieldscurrentlyinthetable?
21. Whenretrievingdatafromadatabasecolumn,whatdatatypeisreturned?
22. WhatisthemaximumlengthofacolumnnameformostearlyxBASEformats?
23. Whatdoestheinstanceofoperatorreallytellyou?
24. AredescendingkeysdirectlysupportedbyxBaseJ?
25. WhatNDXmethodcanyoucalltorefreshindexvaluesstoredintheNDXfile?
26. WhatJavaStringmethodallowsyoutosplitaStringintoanarrayofStringsbasedupona
delimitingString?
27. DoNDXobjectsmonitordatabasechangesmadebyotherprogramsorusers?

94

Chapter1Fundamentals

28. Canyou"undelete"arecordinaDBFfile?Ifso,whyandforhowlong?
29. WhenaNumericfieldisdeclaredwithawidthof6and3decimalplaces,howmanydigits
canexisttotheleftofthedecimalwhenthefieldcontainsanegativevalue?
30. Whendoyouneedtocreateafinalize()methodforyourclass?
31. WhatJavaclassprovidesthereadLine()methodtoobtainalineofinputfromatextfileor
stream?
32. DoxBASEdatafilesprovideanybuiltinmethodofdataintegrity?
33. Whatmustexist,nomatterhowthedataisstored,toprovidedataintegrity?

Chapter2
Chapter2
Mega
Zilli
MegaZ
illioonaire
naireApplica
Applicattion
2.1 WhyThisExample?
Thoseofyouwho havereadtheotherbooksinthisserieswon'tbetheonesaskingthis
question.I'm
answeringthisquestionforthenewcomers.WheneverIcoveranewlanguageor
tool,Iusethisapplicationtoputthefundamentalfeaturesthroughtheirpaces.Itrytoimplement
the application as close as possible to the original implementation covered in ISBN13
9780977086603. Besides being a good test application, having the same application
developed over and over again using different tools and languages allows people who own
legitimatecopiesofthisbookseriestoquicklytransitionbetweenlanguages,tools,andplatforms.
Theapplicationisnotallthatinvolved.Itisasimplelotterytrackingsystemwhichcontains
aprimarydatafileorderedbydrawingdateandtwostatisticsfilesorderedbyelementnumber.
Admittedly,Iusedaverybroaddefinitionofthetermstatisticssincewearesimplykeepingafew
countsandacoupleofpercentages.Theapplicationdoesencompasstheminimumofwhatyou
needtoknowaboutanytoolsetbeforeyoucanrealisticallybeginusingit.Youneedtobeableto
createamenu,animportutility,anentryscreen,adatabrowsingscreen,andsomereports.Ifyou
candoallofthat,youcanprettymuchfigureeverythingelseoutasyougo.
Herearethethreefiles:
Drawing_Data
Draw_dt
Date
No_1
Numeric
No_2
Numeric
No_3
Numeric
No_4
Numeric
No_5
Numeric
Mega_no
Numeric
Drawing_Stats
Elm_no
Hit_count
Last_draw_no
Since_last
Curr_seq
Longest_seq
Pct_hits
Max_btwn
Ave_btwn

2
2
2
2
2
2

Numeric
Numeric
Numeric
Numeric
Numeric
Numeric
Numeric
Numeric
Numeric

k0

2 k0
6
6
6
4
4
8,4
6
8,4

Mega_Stats
Elm_no
Hit_count
Last_draw_no
Since_last
Curr_seq
Longest_seq
Pct_hits
Max_btwn
Ave_btwn

Numeric
Numeric
Numeric
Numeric
Numeric
Numeric
Numeric
Numeric
Numeric

2 k0
6
6
6
4
4
8,4
6
8,4

96

Chapter2MegaZillionaireApplication

Carefulreaderswillnoticethatthelayoutforbothstatsfilesisthesame.WhenIoriginally
designedthisapplicationIdidn't
wanttocomplicateanapplicationIwasgoingtouseforseveral
introductorytexts. Yes,Ihaveenoughtechnicalskillstomergebothofourstatsfilesintoone
withtheadditionofatype orflag bytetosegregatetherecords.Exactlyhowmanypeople
whoarebrandnewtosoftwaredevelopmentorbrandnewtotheconceptofindexedfilesreading
this(orany)bookwillhaveallofthefundamentalskillsneededtojumprightintoanapplication
withasegmentedprimarykeythatspanstwodatatypes?Somestoragesystemswon't
evenallow
suchabeasttoexist.
HopefullyyouspentsometimeworkingthroughprogrammingassignmentsixfromChapter
1.Ifyoudid,thenregardlessofyourpriorskilllevel,youhaveagrasponhowconvolutedthis
demonstrationapplicationwouldgetwhentryingtocreaterecordsinthestatsfilesforthe nth
time.Whileitmaynotbesexy,adesignwhichallowseachstatisticsfiletobedeletedandcreated
fromscratcheachtimeauserchoosestogeneratestatsisbothcleanerandmoreunderstandable.
Let's
faceit:thefirstprogramyouareassignedincollegeprintsHello totheprinter,itdoesn't
solvetheTowerofHanoiproblem.EventuallyyougettotheTowerofHanoiproblem,butyou
don'tstartthere.
With the exception of the Logic book, the books in this series have all been aimed at
professionalprogrammers. Ididn't
givetwofullchaptersofexplanationpriortodroppingthis
exampleonthem.Thisbookisprimarilyaimedatpeoplewhoeitherhavehadoneprogramming
coursecoveringJavaorhavereadaTeach YourselfHowtoBeTotallyUselessin21Daysor
Less typebookandarelookingtoobtainactualskills.Becauseofthisapplicationthebookwill
alsobeusefultoanyonewhoownstherestofthebookseriesandneedstoquicklygetuptospeed
usingxBaseJ,orevenJavaunderLinux.
Unlikepriorbooksinthisseries,thisoneisgoingtodescribewhattheapplicationlookslike
first,thenwewilldiscussit. Themainmenu/formforthisapplicationlooksmuchlikemany
otherapplicationsimplementingtheCUA(CommonUserAccess)interface.Ithasamainmenu
acrossthetop,andthosedropdownwhenyouclickontheentries.

Chapter2MegaZillionaireApplication

Figure1Mainmenuatstartup

Figure2Filemenucontents

97

98

Figure3Reportmenucontents

Figure4Importafterfileimport

Chapter2MegaZillionaireApplication

Chapter2MegaZillionaireApplication

99

Whenyouclickonthe ChooseFile buttonintheImportform,someflavoroftheFile


Chooserwindowwillpopup.Isaysome flavor becauseitwilldependuponwhatLookand
Feellibrariesyouhaveinstalled.Aswewillseelater,thestartupcodesearchesforacoupleof
widelyknown LookandFeelimplementations before defaulting totheincredibly ugly Metal
LookandFeelwhichisthedefaultJavaLookandFeel.Incaseyouarewondering,thisistheFile
Chooser displayed when a Motifbased Look and Feel has been selected. Many people
(developersincluded)willvisithttp://www.javootoo.comtopulldownvariousfreeLookandFeel
packages.YouwillseehowtochangetheLookandFeelwhenwecoverthetestmodulewhich
runsthisapplication.

Figure5FileChooserusedwithimport
Changingjustacouplelinesofcodeinourapplication(andmakingsureanotherJARfileis
included)createsanapplicationwhichlookslikethis:

100

Chapter2MegaZillionaireApplication

Figure6EntryformwithNimrodlookandfeel

Figure7FileChooserwithNimrodlookandfeel
Youmightnoticethatitisn't
onlythecoloringwhichchanges,butthelayoutandstyleof
things.ThecommonJavatoolslikeFileChooseralsoaredramaticallydifferent.NotallLookand
FeelsareavailableforallversionsofJava.

Chapter2MegaZillionaireApplication

101

Figure8Entryform
Onethingwhichmightnotbeobviousisthe Deleted: prompt.Youcannotenteravalue
here,butwhenarecordwhichhasbeenflaggedfordeletionisdisplayed* willdisplayinthis
box. UnlessyouusealotofVCRtypesoftwaretherowofbuttonscontaininglessthanand
greaterthansignsmightnotbeintuitive.Asinglelessthansignmovestowardsthebeginning,
multiplesmovetotheverybeginning.Asinglegreaterthanmovestowardstheendandmultiples
movetotheveryend.
Itprobablywasn't
thebestchoiceoflabels,butOK performseitheranAddoranUpdate
dependinguponwhichmodeyouarecurrentlyin.Thereisnobuttontosetthemodeperse.If
youfindarecordviatheFind buttonoroneofthenavigationbuttons,youwillbeinfindmode.
Bydefaultthescreenstartsoutinaddmode. Ifyouneedtogetbacktoaddmodeyoumust
Clear,whichwillbothclearallentriesonthescreenandresetthescreenbacktoaddmode.

102

Chapter2MegaZillionaireApplication

Figure9Browseform
Althoughitisagainstmyreligiontodesignapplicationswhichloadeveryrecord froma
databaseintoaspreadsheet,thatiswhatendusershavecometoexpectthankstotheworld's
lowest quality software vendor, Microsoft. Nobody with even the tiniest shred of computer
scienceeducationwouldeverconsidergettingusers used toseeingdatadisplayedthisway. It
worksonlyundertheconditionwhichletsitworkhere:averylimitedsetofdatastoredlocally
andaccessreadonly.Ididitbecausemostofyouweregoingtowhineandsnivelaboutwanting
todoit.
Mostofyoureadingthisbookwillnothavehadprofessionalsoftwaredevelopmenttraining.
I cover this topic quite a bit in the OpenVMS Application Developer book (ISBN13
9780977086603) andtheSOAbook(ISBN139780977086665). Thespreadsheetdesign
ishorriblyinefficient.I'm
nottalkingaboutthecodetocreatethespreadsheetitself,I'm
talking
abouttheconceptsbehindthedesign. Itisaresourceintensivepigthatimposesseveredata
accessrestrictionsbyrequiringeitherexclusiveaccesstotheentiredataset,oralivedatamonitor
communicatingwiththedatabasetomonitorforanyandallrecordchanges.

Chapter2MegaZillionaireApplication

103

The xBASE architecture doesn'tlend itself to multiuser access without an intervening


databaseenginelockingallfilesandprovidingallaccess.Wedon't
havethat,sowearealready
living in multiuser Hell, and choose to handle the multiuser problem procedurally by not
creatingthedatafilesonaserverandtellingusersnottorunmultipleinstancesofourapplication.
Itusedtobeeasytorestrictapplicationstononnetworkfilestorage.Youhadtoeitherbe
workingatalargecorporationorbeanUberGeekyourselftoinstallandconfigureaNetwarefile
serverinyourownsmallbusinessorhome. ThenMicrosoftcameoutwiththeirownpathetic
excuseforaNetwarereplacement,loweringtheskilllevelandexponentiallyloweringthebaron
quality.Today,evenablindsquirrelcanfindtheacorn.Forwellunder$1000theaverageuser
canbuyanetworkstoragedevice,plugitin,followashortlistofconfigurationcommands,and
havetheirownfileserver.Securityonthesedevicestendstobealmostnonexistent,giventhat
they are created from ashare everything viewpoint for nontechnical users. Many ofthese
devicescostunder$500andprovidenearly1TBofstorage.UnlikeNetware,thesefileservers
don'tprovide anindexedfilesystem. BtrieveTechnologies, Inc.really needstogetintothis
personalfileservermarket.ThereareprobablystillalotoftoolsouttherewhichsupportBtrieve
andletenduserscreatethingsbypickingandpointing.
Memoryandbandwidthissuessimplycannotbeoverlookedwhendesigninganapplication.I
providedonlyafewhundredrecordsforourtestdatabaseandI'm
creatingthefileslocally.What
happenswhenyoumodifythisapplicationtoopenaDBFandNDXwhichareonaWebsiteor
remotefileserver?Unlessyouareondialup,youprobablyhaveenoughbandwidthtotransfer
fewerthan400records.Howaboutwhenthefileisapproaching2GBandtheenduserisona
satelliteconnectionwitha120MBperdaybandwidthrestriction? Onemustalwaystakesuch
thingsintoconsiderationwhendesigninganapplicationorappletwhichcouldhaveitsdatahosted
remotely.
Ordinarily,ascreenlikethebrowsescreenwouldbedesignedtodisplayfivetotenrecords,
anditwouldhaveasearchprompt. Perhapsthesearchpromptwouldalsohaveacombobox
allowingausertoselectasearchfield,otherwisethesearchwouldbeontheprimarykey.When
auserclickedontheSearchorFindbuttontheapplicationwouldperformindexedlookuplogic
againstthedatabaseanddisplayupto5records.Whilethatdesignmaynotseemasslickasbeing
abletodragascrollbarthroughaspreadsheet,itworksatallresourcelevels.ThepoorSchmoe
whowasgivenacorporatedesktoprunningWindowsXPwithonly256MegofRAMcanuseit
justaseasilyasthepoweruserandtheir2+GhzmulticoreCPUwith4GBorRAM.

Chapter2MegaZillionaireApplication

104
2.2 SupportingClasses
MegaDBF.java
1)
2)
3)
4)
5)
6)
7)
8)
9)
10)
11)
12)
13)
14)
15)
16)
17)
18)
19)
20)
21)
22)
23)
24)
25)
26)
27)
28)
29)
30)
31)
32)
33)
34)
35)
36)
37)
38)
39)
40)
41)
42)
43)
44)
45)
46)
47)
48)
49)
50)
51)
52)
53)
54)
55)
56)
57)
58)
59)

package com.logikal.megazillxBaseJ;
import
import
import
import
import
import

java.io.*;
java.util.*;
org.xBaseJ.*;
org.xBaseJ.fields.*;
org.xBaseJ.Util.*;
org.xBaseJ.indexes.NDX;

public class MegaDBF {


// variables used by the class
//
private DBF aDB = null;
// fields
public DateField Draw_Dt = null;
public NumField No_1 = null;
public NumField No_2 = null;
public NumField No_3 = null;
public NumField No_4 = null;
public NumField No_5 = null;
public NumField Mega_No = null;
// file names
public final String DEFAULT_DB_NAME = "megadb.dbf";
public final String DEFAULT_K0_NAME = "megadbk0.ndx";
// work variables
private boolean continue_flg = true;
private boolean dbOpen
= false;
// result codes
public static final
public static final
public static final
public static final
public static final
public static final
public static final
public static final
public static final
public static final
public static final

int
int
int
int
int
int
int
int
int
int
int

MEGA_SUCCESS
MEGA_DUPE_KEY
MEGA_KEY_NOT_FOUND
MEGA_FILE_OPEN_ERR
MEGA_DEVICE_FULL
MEGA_NO_CURRENT_REC
MEGA_DELETE_FAIL
MEGA_GOTO_FAIL
MEGA_DB_CREATE_FAIL
MEGA_INVALID_DATA
MEGA_END_OF_FILE

=
=
=
=
=
=
=
=
=
=
=

1;
2;
3;
4;
5;
6;
7;
8;
9;
10;
11;

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
Method to populate known class level field objects.
//
This was split out into its own method so it could be used
//
by either the open or the create.
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
private void attach_fields( boolean created_flg) {
try {
if ( created_flg) {
//Create the fields
Draw_Dt
= new DateField( "DrawDt");
No_1
= new NumField( "No1", 2, 0);
No_2
= new NumField( "No2", 2, 0);
No_3
= new NumField( "No3", 2, 0);

Chapter2MegaZillionaireApplication
60)
61)
62)
63)
64)
65)
66)
67)
68)
69)
70)
71)
72)
73)
74)
75)
76)
77)
78)
79)
80)
81)
82)
83)
84)
85)
86)
87)
88)
89)
90)
91)
92)
93)
94)
95)
96)
97)
98)
99)
100)
101)
102)
103)
104)
105)
106)
107)
108)
109)
110)
111)
112)
113)
114)
115)
116)
117)
118)
119)
120)
121)
122)

No_4
No_5
Mega_No

= new NumField( "No4", 2, 0);


= new NumField( "No5", 2, 0);
= new NumField( "MegaNo", 2, 0);

//Add field definitions to database


aDB.addField(Draw_Dt);
aDB.addField(No_1);
aDB.addField(No_2);
aDB.addField(No_3);
aDB.addField(No_4);
aDB.addField(No_5);
aDB.addField(Mega_No);
} else {
Draw_Dt
No_1
No_2
No_3
No_4
No_5
Mega_No
}

=
=
=
=
=
=
=

(DateField) aDB.getField("Drawdt");
(NumField) aDB.getField("No1");
(NumField) aDB.getField("No2");
(NumField) aDB.getField("No3");
(NumField) aDB.getField("No4");
(NumField) aDB.getField("No5");
(NumField) aDB.getField("MegaNo");

} catch ( xBaseJException j){


j.printStackTrace();
} // end catch
catch( IOException i){
i.printStackTrace();
} // end catch IOException
// end attach_fields method

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
Method to close the database.
//
Don't print stack traces here. If close fails it is
//
most likely because the database was never opened.
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public void close_database() {
if (!dbOpen)
return;
try {
if (aDB != null) {
aDB.close();
dbOpen = false;
}
} catch (IOException i) {}
}

// end close_database method

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
Method to create a shiny new database
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public void create_database() {
try {
//Create a new dbf file
aDB=new DBF(DEFAULT_DB_NAME,true);
attach_fields(true);
aDB.createIndex(DEFAULT_K0_NAME,"DrawDt",true,true);
dbOpen = true;
} catch( xBaseJException j){
System.out.println( "xBaseJ Error creating database");
j.printStackTrace();

105

Chapter2MegaZillionaireApplication

106
123)
124)
125)
126)
127)
128)
129)
130)
131)
132)
133)
134)
135)
136)
137)
138)
139)
140)
141)
142)
143)
144)
145)
146)
147)
148)
149)
150)
151)
152)
153)
154)
155)
156)
157)
158)
159)
160)
161)
162)
163)
164)
165)
166)
167)
168)
169)
170)
171)
172)
173)
174)
175)
176)
177)
178)
179)
180)
181)
182)
183)
184)
185)

continue_flg = false;
} // end catch
catch( IOException i){
System.out.println( "IO Error creating database");
i.printStackTrace();
continue_flg = false;
} // end catch IOException
// end create_database method

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
method to retrieve a copy of the DBF object
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public DBF getDBF() {
return aDB;
} // end getDBF method
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
Method to test private flag and see
//
if database has been successfully opened.
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public boolean isOpen() {
return dbOpen;
} // end ok_to_continue method
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
Method to open an existing database and attach primary key
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public int open_database() {
int ret_val = MEGA_SUCCESS;
try {
//Create a new dbf file
aDB=new DBF(DEFAULT_DB_NAME);
attach_fields( false);
aDB.useIndex( DEFAULT_K0_NAME);
dbOpen = true;
reIndex();
// gets around problem with stale index info
} catch( xBaseJException j){
continue_flg = false;
} // end catch
catch( IOException i){
continue_flg = false;
} // end catch IOException
if (!continue_flg) {
continue_flg = true;
System.out.println( "Open failed, attempting create");
create_database();
} // end test for open failure

if (isOpen())
return MEGA_SUCCESS;
else
return MEGA_FILE_OPEN_ERR;
// end open_database method

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
Method to re-index all of the associated index files.

Chapter2MegaZillionaireApplication
186)
187)
188)
189)
190)
191)
192)
193)
194)
195)
196)
197)
198)
199)
200)
201)
202)
203)
204)
205)
206)
207)
208)
209)
210)
211)
212)
213)
214)
215)
216)
217)

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public void reIndex() {
if (aDB != null) {
if (isOpen()) {
try {
NDX n = null;
for (int i=1; i <= aDB.getIndexCount(); i++) {
n = (NDX) aDB.getIndex( i);
n.reIndex();
}
} catch( xBaseJException j){
j.printStackTrace();
} // end catch
catch( IOException i){
i.printStackTrace();
} // end catch IOException
} // end test for open database
} // end test for initialized database object
} // end reIndex method
public int find_EQ_record( String d) {
int ret_val = MEGA_SUCCESS;
boolean perfect_hit;
if (!dbOpen)
return MEGA_FILE_OPEN_ERR;
try {

perfect_hit = aDB.findExact( d);


if ( !perfect_hit) {
System.out.println( "missed");
System.out.println( "Current Record " +
aDB.getCurrentRecordNumber());
218)
ret_val = MEGA_KEY_NOT_FOUND;
219)
}
220)
} catch( xBaseJException j){
221)
System.out.println( j.getMessage());
222)
ret_val = MEGA_KEY_NOT_FOUND;
223)
} // end catch
224)
catch( IOException i){
225)
ret_val = MEGA_KEY_NOT_FOUND;
226)
} // end catch IOException
227)
228)
return ret_val;
229)
} // end find_EQ_record method
230)
231)
public int find_GE_record( String d) {
232)
int ret_val = MEGA_SUCCESS;
233)
234)
if (!dbOpen)
235)
return MEGA_FILE_OPEN_ERR;
236)
237)
try {
238)
aDB.find( d);
239)
} catch( xBaseJException j){
240)
ret_val = MEGA_KEY_NOT_FOUND;
241)
} // end catch
242)
catch( IOException i){
243)
ret_val = MEGA_KEY_NOT_FOUND;
244)
} // end catch IOException
245)
246)
return ret_val;
247)
} // end find_GE_record method

107

Chapter2MegaZillionaireApplication

108
248)
249) }

// end class MegaDBF

Thisisagoodexampleofthefarthestmostofyouwillgowhenwritingyourownclassesfor
applicationreuse,beitreusewithintheapplicationorotherapplications.Onceagainyouwill
seetheopen,close,andcreatemethodshavebeenprovided. Ahiddenattach_fields()method
ensureswealwayshavethesamefieldnames.AttheendofthesourcelistingIaddedmethodsto
findamatchingkeyandfindakeywhichisgreaterthanorequaltoaprovidedkeyvalue.Idid
notprovidemethodsfordeletion,butIdidprovidethereIndex()method.Thenicethingabout
thereIndex()methodisthatyouwillprobablycutandpasteitintoeveryDBFclassyoucreate.
AslongasyoumaketheDBFvariablenameaDBthismethodwillalwaysworkforyou.
Listing lines 56 through 62 might just provide some interesting confusion. I chose the
externallyvisiblecolumnnamesdeliberately. Theyareconsistentwiththenamesusedinthe
otherbooksofthisseries.GiventhenamingrestrictionsxBASEenforcesoncolumnnames,you
willnotethattheactualphysicalcolumnnamesdon't
matchtheexternallyvisible.Ididnotwant
toexperimentwithtryingtomakethe _ characterworkasacolumnname.Somecharactersets
forsomecountriesusethe_ asthede lete character. Iranintothisyearsago. WhenI'm
workingwithsomethinglikearelationalengineIwillusethe_ inacolumnname.Theengine
protectsmefromthepossibilityofthecharactersetchanging.
We should discuss listing line 163 before moving on. I would like to say I originally
designedthisapplicationinsuchawayastoinsulateitfromchangesmadebyothers.Thatwas
myoriginalintention.I,however,didnotoriginallyhavethereIndex()beingcalledoneachopen.
IhadtodothisbecauseofabuginthexBaseJlibrarywhichapparentlyIintroducedwhilefixing
anotherbug.ThankfullytheoriginaldeveloperranthedebuggeronanexampleIprovidedand
foundwherethingswentbad.WhenyouinitiallyopenedanNDXfile,somethingwasnotloaded
correctlywiththeindex.Youcangetaroundthisproblembymovingavalue,anyvalue,tothe
Fieldobjectnamedinthekey,oryoucancallreIndex().CallingreIndex()atthetimeofopen
won't
beabigburdenonmostapplicationshaving fewerthan5000records.Today's
computer
speedsarefastenoughthatyouprobablywon't
evennotice.Unlessyouaretheonlypersonusing
yourapplicationonyourcomputer,youshouldalwayscallreIndex()afteropeninganexisting
NDXfileanyway.
StatElms.java
1)
2)
3)
4)
5)
6)
7)

package com.logikal.megazillxBaseJ;
public class StatElms extends Object {
public int elmNo, hitCount, lastDrawNo, sinceLast, currSeq,
longestSeq, maxBtwn;
public double pctHits, aveBtwn;
} // end StatElms class

Chapter2MegaZillionaireApplication

109

Otherthanchangingthepackagename,thissourcefileisunchangedfromhowitappearedin
otherbooksintheseries.IfyouareunfamiliarwiththeshortcomingsofJava,thisclassfilewill
helppointthemout.Whengeneratingstatistics,weneedanarraytohold abunchofvalues.
Someofthesevaluesareupdatedeachtimeanumberoccursinadrawing,othersareupdated
onlyoncewehaveprocessedalldrawingrecords.Every3GLthebookseriescoversallowsusto
declarearecord/structurecontainingonlythesefields,thencreateanarrayofthatstructure.Java
doesn't
understandtheconceptofrecordsordatastructures,asitiscompletelyObjectOriented.
OOPisgoodforsomethings,butformoststandarddataprocessingtasks,itfails.Whenyouneed
a limited number of records containing a handful of fields OOP fails rather spectacularly.
Ultimately,thecontentsofthisarraygetwrittentooneofthetwostatisticsdatabases.
StatDBF.java
1)
2)
3)
4)
5)
6)
7)
8)
9)
10)
11)
12)
13)
14)
15)
16)
17)
18)
19)
20)
21)
22)
23)
24)
25)
26)
27)
28)
29)
30)
31)
32)
33)
34)
35)
36)
37)
38)
39)
40)
41)
42)
43)

package com.logikal.megazillxBaseJ;
import
import
import
import
import
import

java.io.*;
java.util.*;
org.xBaseJ.*;
org.xBaseJ.fields.*;
org.xBaseJ.Util.*;
org.xBaseJ.indexes.NDX;

public class StatDBF {


// variables used by the class
//
private DBF aDB = null;
// fields
public NumField
public NumField
public NumField
public NumField
public NumField
public NumField
public NumField
public NumField
public NumField

Elm_No = null;
Hit_Count = null;
Last_Draw_No = null;
Since_Last = null;
Curr_Seq = null;
Longest_Seq = null;
Pct_Hits = null;
Max_Btwn = null;
Ave_Btwn = null;

// file names
public String DEFAULT_DB_NAME = null;
public String DEFAULT_K0_NAME = null;
// work variables
private boolean continue_flg = true;
private boolean dbOpen
= false;
// result codes
public static final
public static final
public static final
public static final
public static final
public static final
public static final
public static final

int
int
int
int
int
int
int
int

MEGA_SUCCESS
MEGA_DUPE_KEY
MEGA_KEY_NOT_FOUND
MEGA_FILE_OPEN_ERR
MEGA_DEVICE_FULL
MEGA_NO_CURRENT_REC
MEGA_DELETE_FAIL
MEGA_GOTO_FAIL

=
=
=
=
=
=
=
=

1;
2;
3;
4;
5;
6;
7;
8;

Chapter2MegaZillionaireApplication

110
44)
45)
46)
47)
48)
49)
50)
51)
52)
53)
54)
55)
56)
57)
58)
59)
60)
61)
62)
63)
64)
65)
66)
67)
68)
69)
70)
71)
72)
73)
74)
75)
76)
77)
78)
79)
80)
81)
82)
83)
84)
85)
86)
87)
88)
89)
90)
91)
92)
93)
94)
95)
96)
97)
98)
99)
100)
101)
102)
103)
104)
105)
106)

public static final int MEGA_DB_CREATE_FAIL = 9;


public static final int MEGA_INVALID_DATA
= 10;
public static final int MEGA_END_OF_FILE
= 11;
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
Method to populate known class level field objects.
//
This was split out into its own method so it could be used
//
by either the open or the create.
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
private void attach_fields( boolean created_flg) {
try {
if ( created_flg) {
//Create the fields
Elm_No
= new NumField( "ElmNo",
2, 0);
Hit_Count
= new NumField( "HitCount", 6, 0);
Last_Draw_No
= new NumField( "LstDrwNo", 6, 0);
Since_Last
= new NumField( "SinceLst", 6, 0);
Curr_Seq
= new NumField( "CurrSeq", 4, 0);
Longest_Seq
= new NumField( "LngstSeq", 4, 0);
Pct_Hits
= new NumField( "PctHits", 8, 4);
Max_Btwn
= new NumField( "MaxBtwn", 6, 0);
Ave_Btwn
= new NumField( "AveBtwn", 8, 4);
//Add field definitions to database
aDB.addField(Elm_No);
aDB.addField(Hit_Count);
aDB.addField(Last_Draw_No);
aDB.addField(Since_Last);
aDB.addField(Curr_Seq);
aDB.addField(Longest_Seq);
aDB.addField(Pct_Hits);
aDB.addField(Max_Btwn);
aDB.addField(Ave_Btwn);
} else {
Elm_No
Hit_Count
Last_Draw_No
Since_Last
Curr_Seq
Longest_Seq
Pct_Hits
Max_Btwn
Ave_Btwn
}

=
=
=
=
=
=
=
=
=

(NumField)
(NumField)
(NumField)
(NumField)
(NumField)
(NumField)
(NumField)
(NumField)
(NumField)

aDB.getField("ElmNo");
aDB.getField("HitCount");
aDB.getField("LstDrwNo");
aDB.getField("SinceLst");
aDB.getField("CurrSeq");
aDB.getField("LngstSeq");
aDB.getField("PctHits");
aDB.getField("MaxBtwn");
aDB.getField("AveBtwn");

} catch ( xBaseJException j){


j.printStackTrace();
} // end catch
catch( IOException i){
i.printStackTrace();
} // end catch IOException
// end attach_fields method

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
Method to close the database.
//
Don't print stack traces here. If close fails it is
//
most likely because the database was never opened.
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public void close_database() {
if (!dbOpen)
return;

Chapter2MegaZillionaireApplication
107)
108)
109)
110)
111)
112)
113)
114)
115)
116)
117)
118)
119)
120)
121)
122)
123)
124)
125)
126)
127)
128)
129)
130)
131)
132)
133)
134)
135)
136)
137)
138)
139)
140)
141)
142)
143)
144)
145)
146)
147)
148)
149)
150)
151)
152)
153)
154)
155)
156)
157)
158)
159)
160)
161)
162)
163)
164)
165)
166)
167)
168)
169)

try {
if (aDB != null) {
aDB.close();
dbOpen = false;
}
} catch (IOException i) {}
}

// end close_database method

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
Method to create a shiny new database
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public void create_database( String dbf_name) {
try {
// Define default names
DEFAULT_DB_NAME = dbf_name + ".dbf";
DEFAULT_K0_NAME = dbf_name + "k0.ndx";
//Create a new dbf file
aDB=new DBF(DEFAULT_DB_NAME,true);
attach_fields(true);

aDB.createIndex(DEFAULT_K0_NAME,"ElmNo",true,true);
dbOpen = true;
} catch( xBaseJException j){
System.out.println( "xBaseJ Error creating database");
j.printStackTrace();
continue_flg = false;
} // end catch
catch( IOException i){
System.out.println( "IO Error creating database");
i.printStackTrace();
continue_flg = false;
} // end catch IOException
// end create_database method

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
method to retrieve a copy of the DBF object
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public DBF getDBF() {
return aDB;
} // end getDBF method
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
Method to test private flag and see
//
if database has been successfully opened.
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public boolean isOpen() {
return dbOpen;
} // end ok_to_continue method
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
Method to open an existing database and attach primary key
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public int open_database( String dbf_name) {
int ret_val = MEGA_SUCCESS;
// Define default names
DEFAULT_DB_NAME = dbf_name + ".dbf";
DEFAULT_K0_NAME = dbf_name + "k0.ndx";

111

Chapter2MegaZillionaireApplication

112
170)
171)
172)
173)
174)
175)
176)
177)
178)
179)
180)
181)
182)
183)
184)
185)
186)
187)
188)
189)
190)
191)
192)
193)
194)
195)
196)
197)
198)
199)
200)
201)
202)
203)
204)
205)
206)
207)
208)
209)
210)
211)
212)
213)
214)
215)
216)
217)
218)
219)
220)
221)
222)
223)
224) }

try {
//Create a new dbf file
aDB=new DBF(DEFAULT_DB_NAME);
attach_fields( false);
aDB.useIndex( DEFAULT_K0_NAME);
reIndex();
dbOpen = true;
reIndex();
} catch( xBaseJException j){
continue_flg = false;
} // end catch
catch( IOException i){
continue_flg = false;
} // end catch IOException
if (!continue_flg) {
continue_flg = true;
System.out.println( "Open failed, attempting create");
create_database( dbf_name);
} // end test for open failure

if (isOpen())
return MEGA_SUCCESS;
else
return MEGA_FILE_OPEN_ERR;
// end open_database method

//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
Method to re-index all of the associated index files.
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public void reIndex() {
if (aDB != null) {
if (isOpen()) {
try {
NDX n = null;
for (int i=1; i <= aDB.getIndexCount(); i++) {
n = (NDX) aDB.getIndex( i);
n.reIndex();
}
} catch( xBaseJException j){
j.printStackTrace();
} // end catch
catch( IOException i){
i.printStackTrace();
} // end catch IOException
} // end test for open database
} // end test for initialized database object
} // end reIndex method
// end class StatDBF

YouwillnoticethatIdidn't
providemethodstofindindividualrecordsviaakey. Idid
providethegetDBF()methodsoauseroftheclasscoulddomostofwhat heorshe wanted.
TherewasareIndex()methodprovidedtofacilitateworkingaroundthepreviouslymentioned
bug,butotherwise,thisclassisprettybasic.

Chapter2MegaZillionaireApplication

113

Pleaseallowmetodirectyourattentiontolistinglines119through123and163through168.
Youmayrecallthatbothstatisticsfileshaveexactlythesamelayout. (Ifyoudon't
recallthis
pleaseflipbacktothefirstpageofthechapterandlookattheproposedrecordlayoutsagain.)The
onlygoodwayofreusingadatabaseclassunderthoseconditionsistomaketheopenandcreate
methodssetthefilenamesfortheclass.
AsyouprogressinyourprofessionalcareeryouwillencountermanyOOPprogrammerswho
consideraclassoperatinginthismannercompletesacrilege.Theywouldbewrong.Theywould
alsonotbeproductionprogrammers.Pleasedonotconfusesomeonewhoisalwaysworkingon
new development with someone who is a production programmer. Newdevelopmentonly
peoplecreatethemessproductionpeoplehavetocleanup.
Classeswhichrequireafilenameatthetimeofobjectcreationand/oropenadatabase/file
resourceatthattimearetwoofthemainhallmarksofbadapplicationdesign.Theyseemohso
politicallycorrectwhenyouaresittingthereinschooldoingyourhomework,buttheyareatrain
wreckwaitingtohappeninreallife.Pleasetakeagoodlookatthetopofthissourcefilewhere
the data which will be global for the class instance is declared. Do you see a constructor
anywhereafterthatglobaldatawhichforcesafilenametobesupplied?Forthatmatter,doyou
seeaconstructordeclared?
SomeofyouaregoingtotaketheopportunitytoopenthesourcefortheDBFclass,orflipto
thebackofthisdocumentandpointtotheconstructordocumentationandcryfoul. Youaren't
pointingoutmyerrorwhenyoudothat,youarepointingoutthefactthatyoudonotunderstand
thedifference between application design and library design. Inparticular,lowlevel library
design.Whydoyouthinkthefourexampleprogramsprovidedwiththelibraryitselfaresohard
codedandhavetoberuninaspecificorder?Thisisalowlevellibrary,atleastasfarasxBASE
accessisconcerned.Whileithidessomeoftheuglydetailsfromyou,itcertainlydoesn't
provide
muchinthewayofhighlevelinterfaces.
IhaveseenC/C++codegeneratorswhichtakealistoffieldsalongwiththeirdatatypesanda
listofindexcomponents,thengenerateacompleteclassforyou. Thatclassdoesn't
allowany
lowlevelaccesstoanyportionofthedatastorage.Eachfieldhasitsownuniquelynamedget()
andset()methodsand eachkey hasitsownfindEQ,GT,LTmethods. Inmanycases, the
developerusingtheclasshasnoideawherethedataisstoredorhowitisstored.Ineffect,itis
likethoseI/OmodulesItoldyouaboutearlier.Thetheorybehindthemisthatthedatastorage
couldbechangedwithoutanyonehavingtochangetheapplicationinanyway.Itneverworksin
practice,butitisanicetheory.

Chapter2MegaZillionaireApplication

114

Youaretheoneresponsibleforcreatingthelevelofabstractionnecessaryforyourproject.It
isuptoyoutodecidethedefinitionofcodereuseinyourenvironment.
Someofyouwillbehardcorecoderswhothink thatcuttingandpastingfrominsidethe
editoriswhattheindustrymeanswhentheysaycode reuse. Yourview is thatifonlyyou
knowallofthemodulesaparticularpieceofcodehasbeenpastedinto,thenyoucanneverbe
fired.Youwouldbewrong,butthatwouldbeyourview.
Othersreadingthisbookwilltakeituponthemselvestowriteadatabaseclassgeneratorlike
theoneIdescribed.Youwillalsoincludeextract_to()andcopy()methodstoallowdevelopersto
generateCSVfilesandsafetycopiesofdata. Thestarsmayalignandyoumaychoosetoadd
yourcreationtotheproject,thusimprovingit.
Mostofyouwillfallsomeplaceinthemiddleofthosetwoextremes.Ifyouhadn't
founda
copyofthisbookyouwouldhaveprobablytriedtostumblethroughviathecutandpastemethod,
butnowyouhavesomefilebasedDBFclassesasastartingpoint.Theywillstartoutasdirect
copiesorjustsubsetsoftheclassesIhaveprovided,butasyourcodingandneedsincrease,sowill
the default methodsyouprovide.Youwilleventuallycometorealizethattherealvalueofthis
bookisn'tjustthelibraryexplanation,buttheinstructions
concerninghowtodesignapplications.
MegaXDueElms.java
1)
2)
3)
4)
5)
6)
7)
8)
9)
10)
11)
12)
13)
14)
15)
16)
17)
18)
19)
20)
21)
22)
23)
24)
25)
26)
27)
28)
29)
30)
31)

package com.logikal.megazillxBaseJ;
import java.util.*;
import com.logikal.megazillxBaseJ.StatElms;
public class MegaXDueElms extends StatElms
implements Comparable {
//
// method to cause sort in Descending order
// based upon how many drawings it has been since
// it hit. Number is only "due" if it is past its average.
//
public int compareTo( Object o2) {
MegaXDueElms s2 = (MegaXDueElms) o2;
double o1_value, o2_value;
int ret_val=0;
o1_value = this.aveBtwn - this.sinceLast;
o2_value = s2.aveBtwn - s2.sinceLast;
if ( o2_value > o1_value)
ret_val = 1;
else if ( o2_value < o1_value)
ret_val = -1;
}

return ret_val;
// end compare method

Chapter2MegaZillionaireApplication
32)
33)
34)
35)
36)
37)
38)
39)
40)
41)
42)
43)
44)
45)
46)
47) }

115

public boolean equals( MegaXDueElms m) {


boolean ret_val = false;
if (this.elmNo == m.elmNo)
if (this.hitCount == m.hitCount)
if (this.lastDrawNo == m.lastDrawNo)
if (this.sinceLast == m.sinceLast)
if (this.currSeq == m.currSeq)
if (this.longestSeq == m.longestSeq)
if (this.maxBtwn == m.maxBtwn)
ret_val = true;
}

return ret_val;
// end equals method

// end MegaXDueElms class

Youdidn'treallythinkIwasgoingtoletyouoffwithonlyonerantabouttheshortcomingsof
OOP,didyou?ImustadmitthatinJava1.6thingsgotabitbetter,butyoucan't
simplycodein
Java1.6sinceJava1.4isstillthemostwidelyusedinthefield.Becauseofthat,Ihadtoextend
the StatElms class just to create a class which could implement the Comparable interface.
TechnicallyIdidn'thavetocodetheequals()methodsinceIdidn't
callit,butcompareTo()is
required.
ThemajordownsidetothisdesignisthatIhavetocreateaseparateclassforeverysortorder
needed.I'm
nottalkingaboutanindividualsortcomparefunctionforeachorder,butanactual
classthatIcreateanarrayofandloaddatainto.
SomedocumentationclaimsthattheComparatorinterfacestartedwith1.4.2. Itmayhave,
butonmymachineIcouldn't
getittocompilewhenusingthe1.4switch.AComparatorobject
wouldhaveallowedmetohaveaseparateobjectforeachsortingoftheStatElmsarray,butwould
notrequiredifferentarraysandcopiesofdata. Inthatcase,theclasswouldhaveencompassed
aboutasmuchcode,butyouwouldhavehadlessworktodowithyourassignmentslaterinthis
chapter.Hadwebeenusingversion5orhigherwecouldhavedonethis.

Chapter2MegaZillionaireApplication

116
DueSortCompare.java
1)
2)
3)
4)
5)
6)
7)
8)
9)
10)
11)
12)
13)
14)
15)
16)
17)
18)
19)
20)
21)
22)
23)
24)
25)
26)
27)
28)
29)
30)
31)
32)
33)
34)
35)

package com.logikal.megazillxBaseJ;
import java.util.*;
import com.logikal.megazillxBaseJ.StatElms;
//;;;;;;;;;;;;;;;;;;;;
//
EXAMPLE ONLY
//
Code not actually used in application
//;;;;;;;;;;;;;;;;;;;;
public class DueSortCompare
implements Comparator<StatElms> {
//
// method to cause sort in Descending order
// based upon how many drawings it has been since
// it hit. Number is only "due" if it is past its average.
//
public int compare( StatElms s1, StatElms s2) {
double o1_value, o2_value;
int ret_val=0;
o1_value = s1.aveBtwn - s1.sinceLast;
o2_value = s2.aveBtwn - s2.sinceLast;
if ( o2_value > o1_value)
ret_val = 1;
else if ( o2_value < o1_value)
ret_val = -1;
}
}

return ret_val;
// end compare method

// end DueSortCompare class

ThecalltoArrays.sort()wouldhavehadtheComparatorobjectprovidedasanadditional
parameterratherthanexpectingtheobjectarraytoimplementtheComparableinterface.Itishard
toshowjusthowresourceintensivethisshortcomingiswithouthavinganapplicationthatalready
squeezessystemresourcesandneedingthedatasortedmultipleways.Whatifthedatafilewhich
fedthisarraywasover1GBinsizeandweneededtosortitthreedifferentwaysforareport?
Evenifwedirectlycopiedfromonearrayelementtoanother,wewouldconsumeatleast2GBof
RAMmakingthecopy,thenwewouldhaveto hope garbagecollectionquicklygotaroundto
reclaimingtheoriginalarraysowecouldmakeournextcopy.This,ofcourse,assumesthatwe
hadatleast2GBofRAMavailabletotheJavaVM.
Ididn't
usetheequals()methodinthisapplication,butweshouldtalkaboutitabit.Someof
youmaybehorrifiedtoseeifstatementsnestedthatdeep,but,intruth,itisthesimplestwayto
implementsuchamethod.Ifanyoneofthosefieldsdidn't
match,wecouldstoplooking.You
mayhavenoticedthatIomittedcheckingeitherofthedoublefieldsforbeingequal,butdidyou
hazardaguessastowhy?

Chapter2MegaZillionaireApplication

117

Javaisn't
aperfectlanguage. TheVMbringswithitmuchofthebaggagewehaveseen
throughouttheyearswithfloatingpointnumbers. True,itreducedtheproblemtoexactlytwo
floatingpointimplementations,floatanddouble.Itisalsotruethatthosetwodatatypesusetwo
differentIEEEstandardstohelpreduceproblemswithportingtheVMtovariousplatforms.Even
givenallofthat,westillhavefloatingpointbaggagetodealwith. (AlanguagecalledDIBOL
usedBinaryCodedDecimalorBCDtodofloatingpointmathwhileearlyxBASEformatsstored
everythingincharacterformattocompletelysidesteptheissue.)
Themainbaggageproblemwehavetodealwithisthefactthat1.234willnotequal1.234
most of the time. The IEEE floating point standards are approximations. They introduce
precision errors and rely on rounding rules of the output utilities to correct these errors.
Dependinguponhowyouarrivedat1.234itmaybe1.2344123or1.2344389.Toushumansit
willbedisplayedas1.234,but,toabinaryequalitytestlike==thetwovaluesarenotequal.
Youdon't
knowenoughabouttheapplicationyet,butpctHitsandAveBtwnarecalculated
baseduponthetotalnumberofdrawingsandtheintegervalueswehavealreadytested.Thetotal
numberofdrawingswillbeanintegerwhichisthesameforallelementsandisnotstoredonthe
file.Thebeautyofthisrealityisthatweonlyhavetocomparetheintegerstoseeiftheelements
match.
OurMegaXDueElmsarray isusedtocalculatethevaluesofourDuereport. Ihavenot
providedascreenshotofthisreport,butwewilldiscussitslogicanyway.
2.3 ThePanels
MegaXbaseDuePanel.java
1)
2)
3)
4)
5)
6)
7)
8)
9)
10)
11)
12)
13)
14)
15)
16)
17)
18)
19)
20)
21)
22)

package com.logikal.megazillxBaseJ;
import
import
import
import
import
import

java.awt.*;
java.awt.event.*;
javax.swing.*;
java.util.*;
java.text.*;
java.lang.Integer;

import org.xBaseJ.*;
import org.xBaseJ.fields.*;
import org.xBaseJ.Util.*;
import com.logikal.megazillxBaseJ.MegaXDueElms;
import com.logikal.megazillxBaseJ.StatDBF;
// You need to import the java.sql package to use JDBC
//
import java.sql.*;
import java.io.*;

118

Chapter2MegaZillionaireApplication

23)
24)
25) public class MegaXbaseDuePanel extends JPanel
26)
implements ActionListener {
27)
28)
public final int ELM_COUNT = 57;
// highest number is 56 but I don't
29)
// feel like messing with zero
30)
31)
private JPanel
mainPanel;
32)
private JScrollPane sp;
33)
private JButton
refreshButton;
34)
private JTextArea
dueRptArea;
35)
36)
37)
//;;;;;;;;;;
38)
// Constructor
39)
//;;;;;;;;;;
40)
public MegaXbaseDuePanel( ) {
41)
mainPanel = new JPanel( new GridBagLayout());
42)
GridBagConstraints gbc = new GridBagConstraints();
43)
44)
// Add our refresh button first
45)
// This way we have an object to find the root panel of
46)
//
47)
JPanel buttonPanel = new JPanel();
48)
refreshButton = new JButton("Refresh");
49)
refreshButton.addActionListener( this);
50)
buttonPanel.add( refreshButton, BorderLayout.NORTH);
51)
gbc.anchor
= GridBagConstraints.NORTH;
52)
gbc.gridwidth
= GridBagConstraints.REMAINDER;
53)
mainPanel.add( buttonPanel, gbc);
54)
55)
dueRptArea = new JTextArea();
56)
// Gives you a fixed width font.
57)
dueRptArea.setFont(new Font("Courier", Font.PLAIN, 12));
58)
dueRptArea.setEditable( false);
59)
dueRptArea.setTabSize(4);
60)
dueRptArea.setColumns( 80);
61)
dueRptArea.setRows(120);
62)
dueRptArea.setDoubleBuffered( true);
63)
sp = new JScrollPane( dueRptArea);
64)
sp.setPreferredSize( new Dimension( 500, 300));
65)
66)
67)
mainPanel.add( sp);
68)
add(mainPanel);
69)
setVisible( true);
70)
} // end constructor
71)
72)
73)
public void actionPerformed(ActionEvent event) {
74)
System.out.println( "Entered action event");
75)
76)
generateReport();
77)
78)
} // end actionPerformed method
79)
80)
81)
//;;;;;
82)
// Method which does all of the interesting work.
83)
//;;;;;
84)
private void generateReport() {
85)
StatDBF dDB = new StatDBF();

Chapter2MegaZillionaireApplication
86)
87)
88)
89)
90)
91)
92)
93)
94)
95)
96)
97)
98)
99)
100)
101)
102)
103)
104)
105)
106)
107)
108)
109)
110)
111)
112)
113)
114)
115)
116)
117)
118)
119)
120)
121)
122)

119

StatDBF mDB = new StatDBF();


MegaXDueElms d_elms[] = new MegaXDueElms[ELM_COUNT];
MegaXDueElms m_elms[] = new MegaXDueElms[ELM_COUNT];
//
// These are needed to format the detail lines on the report
//
dueRptArea.setText("");

// clear out in case this isn't first run

try {
// load the array
dDB.open_database("drawst");
for (int i=1; i < ELM_COUNT; i++) {
dDB.getDBF().gotoRecord( i);
d_elms[i] = new MegaXDueElms();
d_elms[i].elmNo = Integer.parseInt(dDB.Elm_No.get().trim());
d_elms[i].hitCount = Integer.parseInt(dDB.Hit_Count.get

().trim());

d_elms[i].lastDrawNo = Integer.parseInt(dDB.Last_Draw_No.get

().trim());

d_elms[i].sinceLast = Integer.parseInt(dDB.Since_Last.get

().trim());

d_elms[i].currSeq = Integer.parseInt(dDB.Curr_Seq.get().trim

());

d_elms[i].longestSeq = Integer.parseInt(dDB.Longest_Seq.get

().trim());

d_elms[i].maxBtwn = Integer.parseInt(dDB.Max_Btwn.get().trim

());

d_elms[i].pctHits = Double.parseDouble(dDB.Pct_Hits.get

().trim());

d_elms[i].aveBtwn = Double.parseDouble(dDB.Ave_Btwn.get());
}
System.out.println("Finished loading array");
// finished with this database
dDB.close_database();
// Sort the array.
Arrays.sort( d_elms, 1, ELM_COUNT-1);

// generate report
DateFormat heading_format = DateFormat.getDateInstance
( DateFormat.SHORT);
123)
NumberFormat nf_elm
= NumberFormat.getInstance();
124)
NumberFormat nf_hits
= NumberFormat.getInstance();
125)
NumberFormat nf_since
= NumberFormat.getInstance();
126)
NumberFormat nf_pct
= NumberFormat.getInstance();
127)
NumberFormat nf_ave
= NumberFormat.getInstance();
128)
129)
nf_elm.setMinimumIntegerDigits(2);
130)
nf_pct.setMinimumFractionDigits(3);
131)
nf_ave.setMinimumFractionDigits(3);
132)
133)
Calendar c
= Calendar.getInstance();
134)
// obtain current date
135)
//
136)
c.setTime( new java.util.Date());
137)
138)
dueRptArea.append( "Date: " + heading_format.format( c.getTime()
)
139)
+ "\n
"

120
140)
141)
142)

Chapter2MegaZillionaireApplication
+ "Due NumbersReport\n");

dueRptArea.append("\n
Regular
Drawing Numbers\n\n");
143)
dueRptArea.append(" NO
Hits Since
Pct_Hits
Ave_Btwn
\n");
144)
dueRptArea.append(" ----- ------------------\n");
145)
146)
String detail_line=null;
147)
int l_x = 1;
148)
while ( l_x < ELM_COUNT ) {
149)
if ((double)d_elms[l_x].sinceLast > d_elms[l_x].aveBtwn) {
150)
detail_line = " " + nf_elm.format( d_elms[ l_x].elmNo)
151)
+ "
" + nf_hits.format( d_elms[ l_x].hitCount)
152)
+ "
" + nf_since.format( d_elms[l_x].sinceLast)
153)
+ "
" + nf_pct.format( d_elms[ l_x].pctHits)
154)
+ "
" + nf_ave.format( d_elms[ l_x].aveBtwn)
155)
+ "\n";
156)
dueRptArea.append( detail_line);
157)
}
158)
l_x++;
159)
} // end while loop
160)
161)
dueRptArea.append( "\n\n");
162)
dueRptArea.append( "\n\n====================================\n
\n");
163)
updateText();
164)
165)
//----166)
// Now process the Mega numbers
167)
//----168)
// load the array
169)
mDB.open_database( "megast");
170)
for (int i=1; i < ELM_COUNT; i++) {
171)
mDB.getDBF().gotoRecord( i);
172)
m_elms[i] = new MegaXDueElms();
173)
m_elms[i].elmNo = Integer.parseInt(mDB.Elm_No.get().trim());
174)
m_elms[i].hitCount = Integer.parseInt(mDB.Hit_Count.get
().trim());
175)
m_elms[i].lastDrawNo = Integer.parseInt(mDB.Last_Draw_No.get
().trim());
176)
m_elms[i].sinceLast = Integer.parseInt(mDB.Since_Last.get
().trim());
177)
m_elms[i].currSeq = Integer.parseInt(mDB.Curr_Seq.get().trim
());
178)
m_elms[i].longestSeq = Integer.parseInt(mDB.Longest_Seq.get
().trim());
179)
m_elms[i].maxBtwn = Integer.parseInt(mDB.Max_Btwn.get().trim
());
180)
m_elms[i].pctHits = Double.parseDouble(mDB.Pct_Hits.get
().trim());
181)
m_elms[i].aveBtwn = Double.parseDouble(mDB.Ave_Btwn.get
().trim());
182)
}
183)
184)
// finished with this database
185)
mDB.close_database();
186)
187)
// Sort the array.
188)
Arrays.sort( m_elms, 1, ELM_COUNT-1);
189)
190)
// generate report

Chapter2MegaZillionaireApplication
191)
192)
193)
194)
195)
196)

121

dueRptArea.append( "Date: " + heading_format.format( c.getTime()


)

+ "\n
+ "Due NumbersReport\n");

"

dueRptArea.append("\n
Mega
Numbers\n\n");
197)
dueRptArea.append(" NO
Hits Since
Pct_Hits
Ave_Btwn
\n");
198)
dueRptArea.append(" ----- ------------------\n");
199)
200)
l_x = 1;
201)
while ( l_x < ELM_COUNT ) {
202)
if ((double)m_elms[l_x].sinceLast > m_elms[l_x].aveBtwn) {
203)
detail_line = " " + nf_elm.format( m_elms[ l_x].elmNo)
204)
+ "
" + nf_hits.format( m_elms[ l_x].hitCount)
205)
+ "
" + nf_since.format( m_elms[l_x].sinceLast)
206)
+ "
" + nf_pct.format( m_elms[ l_x].pctHits)
207)
+ "
" + nf_ave.format( m_elms[ l_x].aveBtwn)
208)
+ "\n";
209)
dueRptArea.append( detail_line);
210)
}
211)
l_x++;
212)
} // end while loop
213)
214)
dueRptArea.append( "\n\n");
215)
dueRptArea.setCaretPosition(0); // scroll back to top
216)
} catch (xBaseJException x) {
217)
} catch (NumberFormatException n) {
218)
n.printStackTrace();
219)
} catch (IOException e) {
220)
}
221)
222)
} // end generateReport method
223)
224)
public void updateText() {
225)
mainPanel.invalidate();
226)
mainPanel.validate();
227)
mainPanel.paintImmediately( mainPanel.getVisibleRect());
228)
mainPanel.repaint();
229)
} // end updateText method
230)
231)
232) } // end MegaXbaseDuePanel class

You will be getting two assignments based upon this module, so I'mgoing to take the
discussionabitslowerthanIhavebeenfortheothermodules.Withtheexceptionoftheimport
function, alloftheothermenuoptionsareimplementedaspanelswhichdisplayonthemain
menupanelusingCardLayout.TheCardLayoutallowsyoutostack1Npanelsontopofeach
otherlikeadeckofcardswhicharefaceup.Youthenshufflethecardyouwanttothetopofthe
faceupdecksoitisdisplayed. Alloftheothercardsareleftintheircurrentstatewhenyou
changethedisplayedcard.Whenyouchoosetoredisplayoneofthecardsyoudonotcreatea
newinstanceofitorreinitializeitscontentsinanyway;itissimplyshownagainasitwaslast
seen.

122

Chapter2MegaZillionaireApplication

EachpanelwhichgetsplacedintotheCardLayoutcan,anddoes,haveitsownlayout. It
couldactuallyhavemanydifferentlayoutsonit,butwehaveonlyonelayoutinusebecausewe
don'thavemuchtodisplay.
Thisparticularpanelhasonlyarefreshbuttontodisplayatthetopofthescreenandatext
areawhichgetsdisplayedinsideofascrollpane.Intruth,itlooksmuchlikethebrowsescreen
snapshotI've
providedyou,onlyithasregulartextinsteadofaspreadsheetinsideofthescroll
pane.
OurpanelusesaGridBagLayout.Don't
askmetotellyouwherethenamecamefromorall
ofthedetails.GridBagLayoutisoneofthefewfunctionallayoutsexistinginJava,atleastinthe
daysofversion1.4.ThetopicoflayoutshasbecomesoheatedanddebatedthattheC++cross
platform GUI library named Qt has released a Java wrapper for its library and many Java
developershave begunmigratingawayfromSwing. Whenitcomestothesubjectoflayout
managersandJava,itlookslikethesubjectwastableduntillaterandlaterstillhasn'tcome.
YoupositionobjectsinaGridBagLayoutviaaGridBagConstraintsobject. Ordinarilyyou
willfillinonlytheanchorandgridwidthvalues,leavingtherestoftheGridBagConstraintsfields
attheirdefaultvalues.Normallygridwidthisanumericconstantsuchas1or2,butitcanbea
coupleofspecial values.OnesuchvalueisREMAINDERasshownonlistingline52.This
tellsthelayoutmanagertheobjectisthelastoneonthecurrentlineofobjectsitisbuilding.
TheanchorportionofGridBagConstraintshasalotofdirectionsoundingconstants,which
canbealittleconfusing. MostpeopleassumethatalloftheNORTHbasedconstraintshave
somethingtodowiththetopofthedisplayandtheSOUTHbasedconstraintshavesomethingto
dowiththebottomofthedisplay.Ingeneral,thisistrue,butnotastrueasitsounds.Tostart
with,theenclosingobject,inthiscaseourJPanel,canchangethecomponent orientationand
insteadoflefttorighttoptobottomthescreenmightbedisplayedrighttoleftbottomtotop.In
anycase,NORTHisassociatedwiththestartingpointandSOUTHwiththefinishingpoint.We
willdiscussthistopicingreaterdetailwhenwecovertheEntryscreen.Ijustwantedtogetyou
thinkingaboutitnow.LayoutsinJavaarenotaneasysubjecttodiscussinabook.Somereaders
willhavetenyearsofcodingundertheirbeltsandafewhundredsourcetemplatessavedondisk;
otherswillhaveonlywrittenHelloWorld.java.
Wecreateourtextareaatlistinglines55through62,thenplaceitinascrollpaneatlisting
line63.MostofwhatIdidwasprettyselfexplanatory.Wedo,however,needtotalkaboutthe
setFont()call.IchosetousethefontnameCourier . Mostsystemswhichhavesomekindof
WebbrowserwillhavesomekindofCourier fontinstalledonthem.Whenyouarecreating
columnarreportsyouneedtohaveafixedwidthfontsothosecolumnshaveachanceatliningup.

Chapter2MegaZillionaireApplication

123

MostJavapuristsreadingthiswillscreamthatIshouldhaveusedthelogicalfontname
Monospaced whichisrequiredtobeimplementedbyallJavaVMs. Thesimpletruthisthat
Monospaced isnotrequiredtobeimplemen ted, itisrequiredtohaveaphysicalfontmapped
toit.Thatfontmayhaveabsolutelynothingtodowithfixedwidthormonospace.EvenCourier
isnotafixedwidthfontwhendealingwithTrueTypefonts.Atcertainpointsizesthingswillline
up,butitwon't
beperfect.Ultimately,thefontyouchoosetouseisuptoyou.Ichoseafont
whichworkswellonmostplatforms. Ifitdoesn't
workwellforyou,changethesourceand
recompileit.
TheworkhorseofthisclassisthegenerateReport()method.Herewereadeachrecordfrom
eachstatfile,savethevaluesintoourarrays,sortourarrays,thenprintourreportintothetext
area.YouwillnotethatIcallupdateText()fromtimetotime.Wheneveryoucallappend()toadd
texttoaJtextArea,thetextisaddedtotheinmemoryobject,butaneventisonlyqueuedto
updateanyassociatedonscreendisplay.ThedisplaymanagerinJavawaitsuntilitthinks the
systemisidletoupdatethedisplay,orfindsitselfforcedtoupdatethedisplay.Thelinesofcode
inupdateText()forcethedisplaymanagertoconsolidatealloftheupdatesanddisplaythem.This
stepdoesslowdownprocessing,soyoushoulddoitonlyatpointsintimewhenyoufeeltheuser
mustseetheprogresswhichhasbeenmade.
Ineedtopointoutonetinylittlethingatlistingline103.YoumaynotgraspwhyIcalled
trim()aftercallingget().TheparseInt()staticmethodthrowsexceptionsifthenumericstringyou
handitcontainsspaces.Idon't
knowwhyitdoesn't
calltrim()onitsown,butitdoesn't
.Asyou
canseebylistingline111,parseDouble()managedtohandlethingsjustfine.
Listinglines123through131containsomethingItrulyhateaboutJava1.4andearlier.The
NumberFormatobjectisveryprimitive.Itdoesprovidemethodstosettheminimumnumberof
fractionaldigits,andminimumnumberofintegerdigits,butithasnoconceptofjustification,fill
character,ordisplaywidth.Ifyoutrytosetboththeintegerandfractiondigitsforacolumn,it
willzerofillonthefrontandforcethedisplaytolooksomethinglikethefollowing.
NO
-30
16
52
23
25
48
43
45
03
46
04

Hits Since
Pct_Hits
Ave_Btwn
---- ------------------0,035
00,010
000.092
009.886
0,036
00,010
000.094
009.583
0,041
00,009
000.108
008.293
0,027
00,014
000.071
013.111
0,040
00,010
000.105
008.525
0,042
00,010
000.110
008.071
0,038
00,011
000.100
009.026
0,031
00,014
000.081
011.290
0,030
00,015
000.079
011.700
0,044
00,011
000.116
007.659
0,031
00,016
000.081
011.290

Chapter2MegaZillionaireApplication

124

NotexactlywhatIwouldcallhumanfriendlyoutput.WediscussedtheFormatterclasson
page54.ThisclassaddedsomethingwhichwasrequiredtobringJavaintothebusinessworld,
theabilitytocreateacolumnarreport.Notoneofyouwouldpayyourcreditcardbillifthetext
swamalloverthepage,butthatisjustwhattheJavadevelopersatSunthoughtweshouldputup
withuntilJava1.5cameout.Ouroutputwilllookasfollows,andwewilllivewithitfornow.
Mega Numbers
NO
-02
27
22
32
31
08
19
05
37
15
14

Hits
---13
7
13
7
7
6
5
6
8
10
7

Since
----11
15
16
18
19
22
23
21
27
25
24

Pct_Hits
Ave_Btwn
--------------0.000
10.222
0.000
11.333
0.000
9.946
0.000
11.333
0.000
10.686
0.000
12.931
0.000
11.750
0.000
9.146
0.000
14.346
0.000
11.242
0.000
9.590

PleasenotethatweaddanewLinecharacteratlistingline155.Wearenotprinting tothe
textarea,weareappending.ItisourresponsibilitytoinserttheappropriatenumberofnewLine
charactersattheappropriateplaces.
Let's
nowdiscussthecalltosort()atlistinglines119and188.Ineededtopassinthesecond
andthirdparameterbecauseIchosetouseelements156insteadof055.Thezeroelementwas
neverfilledinandIdidn't
wanttohavestalegarbageinfluencingtheoutcomeofthesort.Ihave
alreadydiscussedthefactthatIimplementedComparablewithourobjectbecausethecompiler
wouldn'tlet me implement a Comparator object when compiling for version 1.4 targets. In
theory,IguessthemakersofJava1.4didyouafavor.Someofyourassignmentswillbemuch
morecutandpastethanIlike.
Listingline215isatrickyoureallyneedtoknow.Theproblemwithtextareasisthatwhen
yougetdonewritingtothem,thedisplayisatthebottomofthem,notthetop.Youneedtoget
backtothetopofthedisplaysotheuserisn'tcompletelylost.Thislittlestatementhandlesthat.It
resetsthecursorpositionbacktooffsetzero.Thisforcestheeventualscreenupdateshowingthe
topofthetextarea.
Whileitisprobably morecode thanyouwanted tolook at,theDue reportisn'tallthat
complex. I've
alreadyshownyouhowtocreatesimilarreportstothescreenusingxBaseJand
DBFfiles.Allyouhadtolearnherewashowtocreateapanelwithatextarea.Onceyouknew
that,youcouldsimplywalkdownthedatabases,sortthedata,andprintthereport.

Chapter2MegaZillionaireApplication

125

MegaXbaseBrowsePanel.java
1)
2)
3)
4)
5)
6)
7)
8)
9)
10)
11)
12)
13)
14)
15)
16)
17)
18)
19)
20)
21)
22)
23)
24)
25)
26)
27)
28)
29)
30)
31)
32)
33)
34)
35)
36)
37)
38)
39)
40)
41)
42)
43)
44)
45)
46)
47)
48)
49)
50)
51)
52)
53)
54)
55)
56)
57)
58)
59)
60)
61)

package com.logikal.megazillxBaseJ;
import
import
import
import
import
import

java.io.*;
java.awt.*;
java.awt.event.*;
javax.swing.*;
java.util.*;
java.text.*;

import
import
import
import

org.xBaseJ.*;
org.xBaseJ.fields.*;
org.xBaseJ.Util.*;
org.xBaseJ.indexes.NDX;

import com.logikal.megazillxBaseJ.MegaDBF;
public class MegaXbaseBrowsePanel extends JPanel
implements ActionListener {
private
private
private
private
private
private

JPanel
JScrollPane
JButton
JTable
DateFormat
DateFormat

final static

mainPanel;
sp;
refreshButton;
drawTable;
file_date_format = new SimpleDateFormat( "yyyyMMdd");
out_date_format = new SimpleDateFormat( "yyyy/MM/dd");

String columnNames [] = {"Draw_Dt


", "No_1", "No_2"
, "No_3", "No_4", "No_5", "Mega_No"};

//;;;;;;;;;;
// Constructor
//;;;;;;;;;;
public MegaXbaseBrowsePanel( ) {
mainPanel = new JPanel( new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
// Add our refresh button first
// This way we have an object to find the root panel of
//
JPanel buttonPanel = new JPanel();
refreshButton = new JButton("Refresh");
refreshButton.addActionListener( this);
buttonPanel.add( refreshButton, BorderLayout.NORTH);
gbc.anchor
= GridBagConstraints.NORTH;
gbc.gridwidth
= GridBagConstraints.REMAINDER;
mainPanel.add( buttonPanel, gbc);
Object tData [][] = new Object [1][7];

// dummy table.

drawTable = new JTable( tData, columnNames);


drawTable.setAutoResizeMode( JTable.AUTO_RESIZE_ALL_COLUMNS);
sp = new JScrollPane(drawTable);
sp.setPreferredSize( new Dimension( 500, 300));

mainPanel.add( sp);
add(mainPanel);
setVisible( true);
// end constructor

Chapter2MegaZillionaireApplication

126
62)
63)
64)
65)
66)
67)
68)
69)
70)
71)
72)
73)
74)
75)
76)
77)
78)
79)
80)
81)
82)
83)
84)
85)
86)
87)
88)
89)
90)

private Object[][] fetchTableData( ) {


int
l_record_count=0, l_x, l_y, l_try_count;
String
serverResponse=null;
MegaDBF aDB = new MegaDBF();
aDB.open_database();
DBF d = aDB.getDBF();
l_record_count = d.getRecordCount();
System.out.println( "Record count " + l_record_count);
// declare an array to fill based upon rows in table
//
Object tableData [][] = new Object[ l_record_count] [7];
// Fill our new array
//
try {
l_x = 0;
d.startTop();
while ( l_x < l_record_count) {
try {
d.findNext();

tableData[ l_x] [0] = out_date_format.format(


file_date_format.parse
( aDB.Draw_Dt.get()));
91)
tableData[ l_x] [1] = new Integer( aDB.No_1.get().trim());
92)
tableData[ l_x] [2] = new Integer( aDB.No_2.get().trim());
93)
tableData[ l_x] [3] = new Integer( aDB.No_3.get().trim());
94)
tableData[ l_x] [4] = new Integer( aDB.No_4.get().trim());
95)
tableData[ l_x] [5] = new Integer( aDB.No_5.get().trim());
96)
tableData[ l_x] [6] = new Integer( aDB.Mega_No.get().trim());
97)
98)
} catch(ParseException p) {
99)
l_x = l_record_count + 1;
100)
System.out.println( p.toString());
101)
}
102)
103)
l_x++;
104)
105)
} // end while loop
106)
System.out.println( "processed " + l_x + " rows");
107)
}
108)
catch( IOException s) {
109)
l_x = l_record_count + 1;
110)
JRootPane m = (JRootPane)
111)
SwingUtilities.getAncestorOfClass( JRootPane.class,
refreshButton);
112)
if ( m != null)
113)
{
114)
JOptionPane.showMessageDialog(m, s.toString(), "Browse",
115)
JOptionPane.ERROR_MESSAGE);
116)
}
117)
else
118)
System.out.println( "m was null");
119)
}
120)
catch( xBaseJException j) {
121)
l_x = l_record_count + 1;
122)
JRootPane m = (JRootPane)

Chapter2MegaZillionaireApplication

127

123)

SwingUtilities.getAncestorOfClass( JRootPane.class,
refreshButton);
124)
if ( m != null)
125)
{
126)
JOptionPane.showMessageDialog(m, j.toString(), "Browse",
127)
JOptionPane.ERROR_MESSAGE);
128)
}
129)
else
130)
System.out.println( "m was null");
131)
}
132)
133)
aDB.close_database();
134)
135)
return tableData;
136)
} // end fetchTableData method
137)
138)
139)
140)
public void actionPerformed(ActionEvent event) {
141)
System.out.println( "Entered action event");
142)
mainPanel.setVisible( false);
143)
mainPanel.remove( sp);
144)
145)
//
146)
// Build a new table and scroll panel
147)
//
148)
Object tData [] [] = fetchTableData( );
149)
drawTable = new JTable( tData, columnNames);
150)
sp = new JScrollPane(drawTable);
151)
sp.setPreferredSize( new Dimension( 600, 300));
152)
mainPanel.add( sp);
153)
mainPanel.setVisible(true);
154)
} // end actionPerformed method
155)
156)
157)
public void updateText() {
158)
mainPanel.invalidate();
159)
mainPanel.validate();
160)
mainPanel.paintImmediately( mainPanel.getVisibleRect());
161)
mainPanel.repaint();
162)
}
163) } // end MegaXbaseBrowsePanel class definition

OtherthancreatingandmanipulatingaJTableobject,thecodeforthispanel doesn't
work
muchdifferentlyfromtheDuereportpanel.I'm
notfondoflistinglines50through55,butIhad
tohavethem. Remembermyearlierrantaboutrequiringvaluestoinstantiate? Thisisagreat
exampleofhowthatgetsyouintotrouble.Ihadtocreateauselesstablesothescreenwouldbe
somewhatselfexplanatorywhenauserfirstseesit.

128

Chapter2MegaZillionaireApplication

Figure10EmptyBrowsewindow
IfIdidn't
putanemptytableonthescreenauser's
firstthoughtwouldbeRefresh what?
whentheysawthescreen.Thiswouldn't
besobadifyoucouldcleanlyadddatatoit,butthere
wasn'tacleanwaytoachievethatalongwiththerefreshconcept.
WhydoIneedtherefreshconcept?Thispanelattachestothedatabase,loadsalloftherows,
closesthedatabase,thendisplaysthespreadsheet. Itdoesallofthatwhentheuserclicksthe
refresh button. Ithastowaitfortheusertoclickthatbuttonbecausethevery firsttimethe
applicationisruntherewon't
beadatabase.Eveniftherewasadatabase,Iwouldstillneedthe
refreshbuttonsotheusercanverifythatrecordsheorsheaddsviatheEntrypanelareactuallyin
thedatabasealongwithalloftheotherdata.
Listingline63mightlookabitoddifyouhaven't
donemuchwitharraysinJava. This
privatemethodreturnsanewlyallocatedtwodimensionalarrayofobjects.WhileIcouldhave
hardcodedtheseconddimensionatthemethodlevel,Ioptedtoleaveitatthepointofallocation.
Wehavesevenfields,soweneedsevencolumns,butuntilweobtaintherecordcountfromthe
database,wehavenoideahowmanyrowsareneeded.

Chapter2MegaZillionaireApplication

129

NoticehowIloadedthearray. Atlistingline70IobtainareferencetotheinternalDBF
object.Atlistingline84IusethatDBFobjecttoforceindexpositioningtothebeginning.Inside
ofthewhileloopatlistingline87IusetheDBFobjectagaintocallfindNext().
WhydidIgotoallofthistrouble?BecauseIdidn't
addastartTop()orfindNext()wrapper
methodtotheMegaDBF.javasourcefile,andyoucannotpassanullstringorastringofallspaces
toaDBFfindmethodwhenadateornumerickeyisinvolved. Iwantedthedatatoappearin
sortedorder. Whileitispossibletosortatableafteritisloaded,thatisnotthekindofcodeI
wanttowritewhencompilingagainsta1.4target.Java6addedRowSorterandTableRowSorter
tothelanguagetomakesortingdatainatablemucheasier.Youshouldspendsometimereading
uponthosecapabilities.
Pleaselookatlistinglines122through130.It'snota
lotofcodeandmostexampleprograms
youfindwon'tshow
youhowtodoit.TheresultisthatmostexampleprogramsshowaniceGUI
whichwritesallofitserrorstoaterminalwindowauserissupposedlymonitory.Thislittlecode
snippet pops up a message dialog when an error happens. Depending on the classof error
indicated (error, warning, informational, etc.) a different dialog displays. Under normal
circumstancesyouwillalsogetwhateverhasbeenconfiguredastheassociatedsystemsoundfor
thattypeofmessage.
Listinglines148through153containthecodewhichactuallyperformstherefreshfunction.
WecallfetchTableData()tocreateanewdynamicarrayofObjects.Oncewehavethatwecreate
a new JTable object then wrap it in a shiny new JScrollPane and display it. I tweaked the
preferredsizesothedatecolumnwoulddisplaycompletelywithoutrequiringausertomanually
resizeit.
WeactuallynevercalltheupdateText()method.ThatisjustamethodIcarryaroundfrom
panelclasstopanelclass.
MegaXbaseEntryPanel.java
1)
2)
3)
4)
5)
6)
7)
8)
9)
10)
11)
12)
13)
14)
15)
16)

package com.logikal.megazillxBaseJ;
import
import
import
import
import

java.awt.*;
java.awt.event.*;
javax.swing.*;
java.util.*;
java.text.*;

import
import
import
import

org.xBaseJ.*;
org.xBaseJ.fields.*;
org.xBaseJ.Util.*;
org.xBaseJ.indexes.NDX;

import com.logikal.megazillxBaseJ.MegaDBF;
import com.logikal.megazillxBaseJ.StatElms;

Chapter2MegaZillionaireApplication

130
17)
18)
19)
20)
21)
22)
23)
24)
25)
26)
27)
28)
29)
30)
31)
32)
33)
34)
35)
36)
37)
38)
39)
40)
41)
42)
43)
44)
45)
46)
47)
48)
49)
50)
51)
52)
53)
54)
55)
56)
57)
58)
59)
60)
61)
62)
63)
64)
65)
66)
67)
68)
69)
70)
71)
72)
73)
74)
75)
76)
77)
78)
79)

import com.logikal.megazillxBaseJ.StatDBF;
// You need to import the java.sql package to use JDBC
//
import java.sql.*;
import java.io.*;

public class MegaXbaseEntryPanel extends JPanel


implements ActionListener {
public final int ELM_COUNT = 57;
public final int FIND_MODE = 1;
public final int ENTRY_MODE = 0;

// highest number is 56 but I don't


// feel like messing with zero

private
private
private
private
private
private
private
private

JFormattedTextField drawingDate;
JFormattedTextField no1;
JFormattedTextField no2;
JFormattedTextField no3;
JFormattedTextField no4;
JFormattedTextField no5;
JFormattedTextField megaNo;
JTextField deletedFlg;

private
private
private
private
private
private
private
private
private
private

JButton
JButton
JButton
JButton
JButton
JButton
JButton
JButton
JButton
Connection

okButton;
findButton;
genStatsButton;
deleteButton;
clearButton;
nextButton;
prevButton;
firstButton;
lastButton;
conn;

private int currMode;


private
private
private
private

StatElms drawNoStat[], megaNoStat[];


int l_draw_no;
Integer zeroInt;
String errorMsg;

private DateFormat out_format = new SimpleDateFormat( "yyyyMMdd");


private MegaDBF

megaDBF=null;

//
// Fields to hold previous record values from Find
//
private int lNo1, lNo2, lNo3, lNo4, lNo5, lMegaNo;
private java.util.Date dDrawingDate;
private String draw_dt_str;
public MegaXbaseEntryPanel( ) {
//
// This internal class handles verification of numeric
// input for JTextField
//
InputVerifier verifier = new InputVerifier() {

Chapter2MegaZillionaireApplication
80)
81)
82)
83)
84)
85)
86)
87)
88)
89)
90)
91)
92)
93)
94)
95)
96)
97)
98)
99)
100)
101)
102)
103)
104)
105)
106)
107)
108)
109)
110)
111)
112)
113)
114)
115)
116)
117)
118)
119)
120)
121)
122)
123)

public boolean verify(JComponent comp) {


int currVal=0;
boolean returnValue;
JFormattedTextField textField = (JFormattedTextField)comp;
try {
currVal = Integer.parseInt(textField.getText());
returnValue = true;
} catch (NumberFormatException e) {
Toolkit.getDefaultToolkit().beep();
returnValue = false;
}
if (returnValue == true) {
if ( currVal < 1 || currVal >= ELM_COUNT)
returnValue = false;
}
return returnValue;
}

};

public boolean shouldYieldFocus(JComponent input) {


verify(input);
return true;
}

zeroInt = new Integer( 0);


JPanel controlPanel=new JPanel();
controlPanel.setLayout( new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.anchor
gbc.gridwidth

= GridBagConstraints.NORTH;
= GridBagConstraints.REMAINDER;

JLabel panelTitle=new JLabel("Mega Zillionare Entry");


controlPanel.add( panelTitle, gbc);

//
// Our Date prompt
//
gbc.anchor
= GridBagConstraints.WEST;
gbc.gridwidth
= 1;
JLabel dateLabel
= new JLabel( "Drawing Date:");
controlPanel.add( dateLabel);
drawingDate
= new JFormattedTextField( new DecimalFormat
("########"));
124)
drawingDate.setColumns( 10);
125)
gbc.gridwidth
= GridBagConstraints.REMAINDER;
126)
controlPanel.add( drawingDate, gbc);
127)
128)
//
129)
// Prompts for the drawing numbers
130)
//
131)
gbc.anchor
= GridBagConstraints.WEST;
132)
gbc.gridwidth
= 1;
133)
JLabel no1Label = new JLabel( "No 1:");
134)
controlPanel.add( no1Label, gbc);
135)
gbc.gridwidth
= GridBagConstraints.REMAINDER;
136)
no1 = new JFormattedTextField(new DecimalFormat("##"));
137)
no1.setInputVerifier( verifier);
138)
no1.setColumns(2);
139)
controlPanel.add( no1, gbc);
140)
141)

131

132
142)
143)
144)
145)
146)
147)
148)
149)
150)
151)
152)
153)
154)
155)
156)
157)
158)
159)
160)
161)
162)
163)
164)
165)
166)
167)
168)
169)
170)
171)
172)
173)
174)
175)
176)
177)
178)
179)
180)
181)
182)
183)
184)
185)
186)
187)
188)
189)
190)
191)
192)
193)
194)
195)
196)
197)
198)
199)
200)
201)
202)
203)
204)

Chapter2MegaZillionaireApplication
gbc.anchor
= GridBagConstraints.WEST;
gbc.gridwidth
= 1;
JLabel no2Label = new JLabel( "No 2:");
controlPanel.add( no2Label, gbc);
gbc.gridwidth
= GridBagConstraints.REMAINDER;
no2 = new JFormattedTextField( new DecimalFormat("##"));
no2.setInputVerifier( verifier);
no2.setColumns(2);
controlPanel.add( no2, gbc);
gbc.anchor
= GridBagConstraints.WEST;
gbc.gridwidth
= 1;
JLabel no3Label = new JLabel( "No 3:");
controlPanel.add( no3Label, gbc);
gbc.gridwidth
= GridBagConstraints.REMAINDER;
no3 = new JFormattedTextField( new DecimalFormat("##"));
no3.setInputVerifier( verifier);
no3.setColumns(2);
controlPanel.add( no3, gbc);
gbc.anchor
= GridBagConstraints.WEST;
gbc.gridwidth
= 1;
JLabel no4Label = new JLabel( "No 4:");
controlPanel.add( no4Label, gbc);
gbc.gridwidth
= GridBagConstraints.REMAINDER;
no4 = new JFormattedTextField(new DecimalFormat("##"));
no4.setInputVerifier( verifier);
no4.setColumns(2);
controlPanel.add( no4, gbc);
gbc.anchor
= GridBagConstraints.WEST;
gbc.gridwidth
= 1;
JLabel no5Label = new JLabel( "No 5:");
controlPanel.add( no5Label, gbc);
gbc.gridwidth
= GridBagConstraints.REMAINDER;
no5 = new JFormattedTextField(new DecimalFormat("##"));
no5.setInputVerifier( verifier);
no5.setColumns(2);
controlPanel.add( no5, gbc);
gbc.anchor
= GridBagConstraints.WEST;
gbc.gridwidth
= 1;
JLabel megaNoLabel = new JLabel( "Mega No:");
controlPanel.add( megaNoLabel, gbc);
gbc.gridwidth
= GridBagConstraints.REMAINDER;
megaNo = new JFormattedTextField(new DecimalFormat("##"));
megaNo.setInputVerifier( verifier);
megaNo.setColumns(2);
controlPanel.add( megaNo, gbc);
gbc.anchor
= GridBagConstraints.WEST;
gbc.gridwidth
= 1;
JLabel deletedFlgLabel = new JLabel( "Deleted:");
controlPanel.add( deletedFlgLabel, gbc);
gbc.gridwidth
= GridBagConstraints.REMAINDER;
deletedFlg = new JTextField(1);
deletedFlg.setEditable(false);
controlPanel.add( deletedFlg, gbc);

Chapter2MegaZillionaireApplication
205)
206)
207)
208)
209)
210)
211)
212)
213)
214)
215)
216)
217)
218)
219)
220)
221)
222)
223)
224)
225)
226)
227)
228)
229)
230)
231)
232)
233)
234)
235)
236)
237)
238)
239)
240)
241)
242)
243)
244)
245)
246)
247)
248)
249)
250)
251)
252)
253)
254)
255)
256)
257)
258)
259)
260)
261)
262)
263)
264)
265)
266)
267)

//
// The Clear Button
//
gbc.anchor
= GridBagConstraints.SOUTHWEST;
gbc.gridwidth
= 1;
clearButton = new JButton("Clear");
clearButton.addActionListener( this);
controlPanel.add( clearButton, gbc);
//
// The Find Button
//
gbc.anchor
= GridBagConstraints.SOUTHWEST;
gbc.gridwidth
= 1;
findButton = new JButton("Find");
findButton.addActionListener( this);
controlPanel.add( findButton, gbc);
//
// The Delete Button
//
gbc.anchor
= GridBagConstraints.SOUTHWEST;
gbc.gridwidth
= 1;
deleteButton = new JButton("Delete");
deleteButton.addActionListener( this);
controlPanel.add( deleteButton, gbc);
//
// The Gen Stats Button
//
gbc.anchor
= GridBagConstraints.SOUTH;
gbc.gridwidth
= 1;
genStatsButton = new JButton("Gen Stats");
genStatsButton.addActionListener( this);
controlPanel.add( genStatsButton, gbc);
//
//
The OK Button
//
gbc.anchor
= GridBagConstraints.SOUTHEAST;
gbc.gridwidth
= GridBagConstraints.REMAINDER;
okButton = new JButton("OK");
okButton.addActionListener( this);
controlPanel.add( okButton, gbc);
//
// The First Button
//
gbc.anchor
= GridBagConstraints.LINE_START;
gbc.gridwidth
= 1;
gbc.weightx
= 1.0;
firstButton = new JButton("<<<");
firstButton.addActionListener( this);
controlPanel.add( firstButton, gbc);
//
// The Prev Button
//
gbc.anchor
= GridBagConstraints.SOUTH;
gbc.gridwidth
= 1;
gbc.weightx
= 0.0;
prevButton = new JButton(" < ");

133

Chapter2MegaZillionaireApplication

134
268)
269)
270)
271)
272)
273)
274)
275)
276)
277)
278)
279)
280)
281)
282)
283)
284)
285)
286)
287)
288)
289)
290)
291)
292)
293)
294)
295)
296)
297)
298)
299)
300)
301)
302)
303)
304)
305)
306)
307)
308)
309)
310)
311)
312)
313)
314)
315)
316)
317)
318)
319)
320)
321)
322)
323)
324)
325)
326)
327)
328)
329)
330)

prevButton.addActionListener( this);
controlPanel.add( prevButton, gbc);
//
// The next Button
//
gbc.anchor
= GridBagConstraints.SOUTH;
gbc.gridwidth
= GridBagConstraints.RELATIVE;
nextButton = new JButton(" > ");
nextButton.addActionListener( this);
controlPanel.add( nextButton, gbc);
//
// The Last Button
//
gbc.anchor
= GridBagConstraints.SOUTHEAST;
gbc.gridwidth
= GridBagConstraints.REMAINDER;
gbc.weightx
= 1.0;
lastButton = new JButton(">>>");
lastButton.addActionListener( this);
controlPanel.add( lastButton, gbc);
// Show the world
//
add( controlPanel);
drawingDate.requestFocus();
setVisible( true);
//
Default mode is entry
//
currMode = ENTRY_MODE;
megaDBF = new MegaDBF();
}

// end constructor

//;;;;;
// Method to handle button actions.
// Java has a real failing here. This code would be so much
// cleaner if a switch could be used on an object or string
//;;;;;
public void actionPerformed(ActionEvent e) {
if ( e.getSource() == okButton) {
if ( currMode == ENTRY_MODE) {
if (addRecord() == false)
display_error_msg( "Add Error");
} else {
if (updateRecord() == false)
display_error_msg( "Update Error");
}
return;
} // end test for okButton
if ( e.getSource() == deleteButton) {
if ( currMode != FIND_MODE) {
display_error_msg( "Must find before deleting");
} else {
if ( deleteRecord() != true)
display_error_msg( "Delete Error");

Chapter2MegaZillionaireApplication
331)
332)
333)
334)
335)
336)
337)
338)
339)
340)
341)
342)
343)
344)
345)
346)
347)
348)
349)
350)
351)
352)
353)
354)
355)
356)
357)
358)
359)
360)
361)
362)
363)
364)
365)
366)
367)
368)
369)
370)
371)
372)
373)
374)
375)
376)
377)
378)
379)
380)
381)
382)
383)
384)
385)
386)
387)
388)
389)
390)
391)
392)
393)

}
return;
// end test for deleteButton

if ( e.getSource() == findButton) {
currMode = FIND_MODE;
if ( findRecord() == false)
display_error_msg( "Drawing Not Found");
return;
} // end test for findButton
if ( e.getSource() == genStatsButton) {
createStatsTable();
return;
} // end test for clear button
if ( e.getSource() == clearButton) {
currMode = ENTRY_MODE;
draw_dt_str = " ";
lNo1 = lNo2 = lNo3 = lNo4 = lNo5 = lMegaNo = 0;
no1.setValue( null);
no2.setValue( null);
no3.setValue( null);
no4.setValue( null);
no5.setValue( null);
megaNo.setValue( null);
deletedFlg.setText("");
drawingDate.setValue( null);
} // end test for clear button
if ( e.getSource() == firstButton) {
currMode = FIND_MODE;
if ( firstRecord() == false)
display_error_msg( "Drawing Not Found");
return;
} // end test for firstButton
if ( e.getSource() == prevButton) {
if ( prevRecord() == false)
display_error_msg( "Drawing Not Found");
return;
} // end test for prevButton
if ( e.getSource() == nextButton) {
if ( nextRecord() == false)
display_error_msg( "Drawing Not Found");
return;
} // end test for nextButton
if ( e.getSource() == lastButton) {
currMode = FIND_MODE;
if ( lastRecord() == false)
display_error_msg( "Drawing Not Found");
return;
} // end test for lastButton
}

// end actionPerformed method

/*;;;;;
* method to add a record to the database
*;;;;;
*/

135

Chapter2MegaZillionaireApplication

136
394)
395)
396)
397)
398)
399)
400)
401)
402)
403)
404)
405)
406)
407)
408)
409)
410)
411)
412)
413)
414)
415)
416)
417)
418)
419)
420)
421)
422)
423)
424)
425)
426)
427)
428)
429)
430)
431)
432)
433)
434)
435)
436)
437)
438)
439)
440)
441)
442)
443)
444)
445)
446)
447)
448)
449)
450)
451)
452)
453)
454)
455)
456)

private boolean addRecord() {


boolean retVal = false;
int
l_x;
String ins_str;
String localDateStr=null;
// Obtain values from the panel
//
draw_dt_str = drawingDate.getText();
pad_draw_dt_str();
if (!is_record_valid())
return false;
try {
localDateStr = out_format.format( out_format.parse(draw_dt_str));
}
catch( ParseException p) {
display_error_msg( "Error parsing date" + p.toString());
}
if ( localDateStr == null)
return false;
// Attempt to add the record
//
try {
megaDBF.open_database();
megaDBF.No_1.put( no1.getText().trim());
megaDBF.No_2.put( no2.getText().trim());
megaDBF.No_3.put( no3.getText().trim());
megaDBF.No_4.put( no4.getText().trim());
megaDBF.No_5.put( no5.getText().trim());
megaDBF.Mega_No.put( megaNo.getText().trim());
megaDBF.Draw_Dt.put( localDateStr);
megaDBF.getDBF().write();
megaDBF.close_database();
retVal = true;
currMode = ENTRY_MODE;
draw_dt_str = " ";
lNo1 = lNo2 = lNo3 = lNo4 = lNo5 = lMegaNo = 0;
no1.setValue( null);
no2.setValue( null);
no3.setValue( null);
no4.setValue( null);
no5.setValue( null);
megaNo.setValue( null);
deletedFlg.setText(" ");
drawingDate.setValue( null);
} catch( xBaseJException s) {
display_error_msg( "Error adding record " + s.toString());
} catch( IOException i) {
display_error_msg( "Error adding record " + i.toString());
}

return retVal;
// end addRecord method

/*;;;;;

Chapter2MegaZillionaireApplication
457)
458)
459)
460)
461)
462)
463)
464)
465)
466)
467)
468)
469)
470)
471)
472)
473)
474)
475)
476)
477)
478)
479)
480)
481)
482)
483)
484)
485)
486)
487)
488)
489)
490)
491)
492)
493)
494)
495)
496)
497)
498)
499)
500)
501)
502)
503)
504)
505)
506)
507)
508)
509)
510)
511)
512)
513)
514)
515)
516)
517)
518)
519)

137

* method to update a record in the database


*;;;;;
*/
private boolean updateRecord() {
boolean retVal = false;
int
l_x;
String originalDrawDtStr;
String localDateStr=null;
String upd_str;
if (!is_record_valid())
return false;
// Obtain values from the panel
//
originalDrawDtStr
= draw_dt_str; // save original value
draw_dt_str = drawingDate.getText();
pad_draw_dt_str();
try {
localDateStr = out_format.format( out_format.parse(draw_dt_str));
} catch( ParseException p) {
display_error_msg( "Error parsing date " + p.toString());
}
if (localDateStr == null)
return false;
if (localDateStr.compareTo( originalDrawDtStr) != 0) {
display_error_msg( "Not allowed to change drawing date");
return false;
}
// Attempt to add the record
//
try {
megaDBF.open_database();
megaDBF.find_EQ_record( localDateStr);
megaDBF.No_1.put( no1.getText().trim());
megaDBF.No_2.put( no2.getText().trim());
megaDBF.No_3.put( no3.getText().trim());
megaDBF.No_4.put( no4.getText().trim());
megaDBF.No_5.put( no5.getText().trim());
megaDBF.Mega_No.put( megaNo.getText().trim());
megaDBF.Draw_Dt.put( localDateStr);
megaDBF.getDBF().update();
megaDBF.close_database();
retVal = true;
currMode = ENTRY_MODE;
draw_dt_str = " ";
lNo1 = lNo2 = lNo3 = lNo4 = lNo5 = lMegaNo = 0;
no1.setValue( null);
no2.setValue( null);
no3.setValue( null);
no4.setValue( null);
no5.setValue( null);
megaNo.setValue( null);
deletedFlg.setText(" ");
drawingDate.setValue( null);
} catch( xBaseJException s) {
display_error_msg( "Error updating record " + s.toString());

Chapter2MegaZillionaireApplication

138
520)
521)
522)
523)
524)
525)
526)
527)
528)
529)
530)
531)
532)
533)
534)
535)
536)
537)
538)
539)
540)
541)
542)
543)
544)
545)
546)
547)
548)
549)
550)
551)
552)
553)
554)
555)
556)
557)
558)
559)
560)
561)
562)
563)
564)
565)
566)
567)
568)
569)
570)
571)
572)
573)
574)
575)
576)
577)
578)
579)
580)
581)
582)

} catch( IOException i) {
display_error_msg( "Error adding record " + i.toString());
}

return retVal;
// end updateRecord method

/*;;;;;
* method to delete a record which has been found
*;;;;;
*/
private boolean deleteRecord() {
boolean retVal = false;
int
l_x;
String del_str;
String localDateStr=null;
draw_dt_str = drawingDate.getText();
//pad_draw_dt_str();
try {
localDateStr = out_format.format( out_format.parse(draw_dt_str));
}
catch( ParseException p) {
display_error_msg( "Error parsing date " + p.toString());
}
if (localDateStr == null)
return false;
try {
megaDBF.open_database();
megaDBF.find_EQ_record( localDateStr);
megaDBF.getDBF().delete();
megaDBF.close_database();
retVal = true;
currMode = ENTRY_MODE;
draw_dt_str = " ";
lNo1 = lNo2 = lNo3 = lNo4 = lNo5 = lMegaNo = 0;
no1.setValue( null);
no2.setValue( null);
no3.setValue( null);
no4.setValue( null);
no5.setValue( null);
megaNo.setValue( null);
drawingDate.setValue( null);
deletedFlg.setText(" ");
} catch( xBaseJException s) {
display_error_msg( "Error deleting record " + s.toString());
} catch( IOException i) {
display_error_msg( "Error adding record " + i.toString());
}
}

return retVal;
// end deleteRecord method

/*;;;;;
* method to find a record based upon drawing date
*;;;;;
*/
private boolean findRecord() {

Chapter2MegaZillionaireApplication
583)
584)
585)
586)
587)
588)
589)
590)
591)
592)
593)
594)
595)
596)
597)
598)
599)
600)
601)
602)
603)
604)
605)
606)
607)
608)
609)
610)
611)
612)
613)
614)
615)
616)
617)
618)
619)
620)
621)
622)
623)

139

int
l_x=0;
boolean retVal=false;
String find_str;
String localDateStr=null;
draw_dt_str = drawingDate.getText();
pad_draw_dt_str();
try {
localDateStr = out_format.format( out_format.parse(draw_dt_str));
}
catch( ParseException p) {
display_error_msg( "Error parsing date " + p.toString());
localDateStr = null;
}
if (localDateStr == null)
return false;
try {
megaDBF.open_database();
l_x = megaDBF.find_GE_record( localDateStr);

());

dDrawingDate
lNo1
lNo2
lNo3
lNo4
lNo5
lMegaNo

=
=
=
=
=
=
=

out_format.parse(
Integer.parseInt(
Integer.parseInt(
Integer.parseInt(
Integer.parseInt(
Integer.parseInt(
Integer.parseInt(

megaDBF.Draw_Dt.get());
megaDBF.No_1.get().trim());
megaDBF.No_2.get().trim());
megaDBF.No_3.get().trim());
megaDBF.No_4.get().trim());
megaDBF.No_5.get().trim());
megaDBF.Mega_No.get().trim

if (megaDBF.getDBF().deleted())
deletedFlg.setText("*");
else
deletedFlg.setText(" ");
megaDBF.close_database();

// Update the screen


//
drawingDate.setValue( new Integer(out_format.format
(dDrawingDate)));
624)
draw_dt_str = out_format.format( dDrawingDate);
625)
no1.setValue( new Integer(lNo1));
626)
no2.setValue( new Integer(lNo2));
627)
no3.setValue( new Integer(lNo3));
628)
no4.setValue( new Integer(lNo4));
629)
no5.setValue( new Integer(lNo5));
630)
megaNo.setValue( new Integer(lMegaNo));
631)
retVal = true;
632)
} catch( ParseException p) {
633)
display_error_msg( "Error parsing date " + draw_dt_str +
634)
"Error was " + p.toString());
635)
draw_dt_str = " ";
636)
lNo1 = lNo2 = lNo3 = lNo4 = lNo5 = lMegaNo = 0;
637)
no1.setValue( null);
638)
no2.setValue( null);
639)
no3.setValue( null);
640)
no4.setValue( null);
641)
no5.setValue( null);
642)
megaNo.setValue( null);
643)
deletedFlg.setText(" ");

Chapter2MegaZillionaireApplication

140
644)
645)
646)
647)
648)
649)
650)
651)
652)
653)
654)
655)
656)
657)
658)
659)
660)
661)
662)
663)
664)
665)
666)
667)
668)
669)
670)
671)
672)
673)
674)
675)
676)
677)
678)
679)
680)
681)
682)
683)
684)
685)
686)
687)
688)
689)
690)
691)
692)
693)
694)
695)
696)
697)
698)
699)
700)
701)
702)
703)
704)
705)
706)

drawingDate.setValue( null);
} catch( NumberFormatException n) {
display_error_msg( "Error parsing date " + draw_dt_str +
"Error was " + n.toString());
draw_dt_str = " ";
lNo1 = lNo2 = lNo3 = lNo4 = lNo5 = lMegaNo = 0;
no1.setValue( null);
no2.setValue( null);
no3.setValue( null);
no4.setValue( null);
no5.setValue( null);
megaNo.setValue( null);
deletedFlg.setText(" ");
drawingDate.setValue( null);
}
return retVal;
// end findRecord method

//;;;;;
// Internal method to pad the display of numbers to 2 digits
//;;;;;
private void pad_draw_dt_str() {
draw_dt_str = draw_dt_str.trim();
switch( draw_dt_str.length()) {
case 4:
// only year provided
draw_dt_str += "0101";
break;
case 6:
// year and month
draw_dt_str += "01";
break;
case 8:
// full date, no padding
break;
default:
// no idea, pick a default
draw_dt_str = "19900101";
break;
} // end switch of length
}

// end pad_draw_dt_str method

/*;;;;;
* Begin logic to rebuild the Stats tables
*;;;;;
*/
public void createStatsTable()
{
int
l_x;
int
l_record_count;
l_draw_no = 0;
//
// It will seem odd to you, but you have to do 2
// allocations when working with a non native array.
//
//
drawNoStat = new StatElms[ ELM_COUNT];
for (l_x=1; l_x < ELM_COUNT; l_x++ ) {
drawNoStat[ l_x]
= new StatElms();
drawNoStat[ l_x].elmNo
= l_x;
} // end for loop to init stats

Chapter2MegaZillionaireApplication
707)
708)
709)
710)
711)
712)
713)
714)
715)
716)
717)
718)
719)
720)
721)
722)
723)
724)
725)
726)
727)
728)
729)
730)
731)
732)
733)
734)
735)
736)
737)
738)
739)
740)
741)
742)
743)
744)
745)
746)
747)
748)
749)
750)
751)
752)
753)
754)
755)
756)
757)
758)
759)
760)
761)
762)
763)

141

megaNoStat = new StatElms[ ELM_COUNT];


System.out.println( "Initializing megaNoStat");
for (l_x=1; l_x < ELM_COUNT; l_x++ ) {
megaNoStat[ l_x]
= new StatElms();
megaNoStat[ l_x].elmNo
= l_x;
} // end for loop to init stats
megaDBF.open_database();
// Create a statement
try {
l_record_count = megaDBF.getDBF().getRecordCount();
for (l_x=1; l_x <= l_record_count; l_x++) {
megaDBF.getDBF().gotoRecord( l_x);
l_draw_no++;
updateDrawStats( Integer.parseInt( megaDBF.No_1.get().trim

()));

updateDrawStats( Integer.parseInt( megaDBF.No_2.get().trim

()));

updateDrawStats( Integer.parseInt( megaDBF.No_3.get().trim

()));

updateDrawStats( Integer.parseInt( megaDBF.No_4.get().trim

()));

updateDrawStats( Integer.parseInt( megaDBF.No_5.get().trim

()));

updateMegaStats( Integer.parseInt( megaDBF.Mega_No.get

().trim()));

if (l_draw_no % 100 == 0)
System.out.println( "Processed " + l_x + " Records");
// end for l_y loop

megaDBF.close_database();
} catch( xBaseJException s) {
display_error_msg( "Error reading DBF " + s.toString());
} catch( IOException i) {
display_error_msg( "Error reading DBF " + i.toString());
} catch( NumberFormatException n) {
display_error_msg( "Error parsing integer " + n.toString());
}
System.out.println( "Processed " + l_draw_no + " Records");
writeDrawStats( );
}

// end createStatsTable method

//;;;;;;;;;;
// Method to actually write all of our Stats records to the database.
//;;;;;;;;;;
private void writeDrawStats( ) {
int l_x, l_missed, l_y;
StatDBF drawStatDBF=null;
StatDBF megaStatDBF=null;
drawStatDBF = new StatDBF();
megaStatDBF = new StatDBF();
drawStatDBF.create_database( "drawst");
megaStatDBF.create_database( "megast");

Chapter2MegaZillionaireApplication

142
764)
765)
766)
767)
768)
769)
770)
771)
772)
773)
774)
775)
776)
777)
778)
779)
780)
781)
782)
783)
784)
785)
786)
787)
788)
789)
790)
791)
792)
793)
794)
795)
796)
797)
798)
799)
800)
801)
802)
803)
804)
805)
806)
807)
808)
809)
810)
811)
812)
813)
814)
815)
816)
817)
818)
819)
820)
821)
822)
823)
824)

System.out.println( "Writing mega stats records");


System.out.println( "Value of l_draw_no " + l_draw_no);
for (l_x=1; l_x < ELM_COUNT; l_x++ ) {
drawNoStat[ l_x].pctHits = (double)( megaNoStat[ l_x].hitCount) /
(double)( l_draw_no);
l_missed
= l_draw_no - megaNoStat[ l_x].hitCount;
megaNoStat[ l_x].aveBtwn = (double)( l_missed) /
(double)( drawNoStat[ l_x].hitCount);
megaNoStat[ l_x].sinceLast = l_draw_no - megaNoStat
[l_x].lastDrawNo;
try {
megaStatDBF.Elm_No.put( megaNoStat[ l_x].elmNo);
megaStatDBF.Hit_Count.put( megaNoStat[ l_x].hitCount);
megaStatDBF.Last_Draw_No.put( megaNoStat[ l_x].lastDrawNo);
megaStatDBF.Since_Last.put( megaNoStat[ l_x].sinceLast);
megaStatDBF.Curr_Seq.put( megaNoStat[ l_x].currSeq);
megaStatDBF.Longest_Seq.put( megaNoStat[ l_x].longestSeq);
megaStatDBF.Pct_Hits.put( megaNoStat[ l_x].pctHits);
megaStatDBF.Max_Btwn.put( megaNoStat[ l_x].maxBtwn);
megaStatDBF.Ave_Btwn.put( megaNoStat[ l_x].aveBtwn);
megaStatDBF.getDBF().write();

} catch( xBaseJException s) {
display_error_msg( "Error adding Mega stat record " +
s.toString());
} catch( IOException i) {
display_error_msg( "Error reading DBF " + i.toString());
}
// end for l_x loop

System.out.println( "Writing Drawing stats records");


for (l_x=1; l_x < ELM_COUNT; l_x++ ) {
drawNoStat[ l_x].pctHits = (double)( drawNoStat[ l_x].hitCount) /
(double)( l_draw_no);
l_missed
= l_draw_no - drawNoStat[ l_x].hitCount;
drawNoStat[ l_x].aveBtwn = (double)( l_missed) /
(double)( drawNoStat[ l_x].hitCount);
drawNoStat[ l_x].sinceLast = l_draw_no - drawNoStat
[l_x].lastDrawNo;
try {
drawStatDBF.Elm_No.put( drawNoStat[ l_x].elmNo);
drawStatDBF.Hit_Count.put( drawNoStat[ l_x].hitCount);
drawStatDBF.Last_Draw_No.put( drawNoStat[ l_x].lastDrawNo);
drawStatDBF.Since_Last.put( drawNoStat[ l_x].sinceLast);
drawStatDBF.Curr_Seq.put( drawNoStat[ l_x].currSeq);
drawStatDBF.Longest_Seq.put( drawNoStat[ l_x].longestSeq);
drawStatDBF.Pct_Hits.put( drawNoStat[ l_x].pctHits);
drawStatDBF.Max_Btwn.put( drawNoStat[ l_x].maxBtwn);
drawStatDBF.Ave_Btwn.put( drawNoStat[ l_x].aveBtwn);
drawStatDBF.getDBF().write();
} catch( xBaseJException s) {
display_error_msg( "Error adding Drawing Stat record " +
s.toString());
} catch( IOException i) {
display_error_msg( "Error reading DBF " + i.toString());

Chapter2MegaZillionaireApplication
825)
826)
827)
828)
829)

143

}
// end for l_x loop

System.out.println( "All Stats records successfully written");


JRootPane j = (JRootPane) SwingUtilities.getAncestorOfClass
( JRootPane.class, no1);
830)
if ( j != null) {
831)
JOptionPane.showMessageDialog(j,"All Stats records successfully
written",
832)
"Stats Generated",
JOptionPane.PLAIN_MESSAGE);
833)
}
834)
else
835)
System.out.println( "j was null");
836)
837)
} // end writeDrawStats method
838)
839)
840)
private void updateDrawStats( int num_sub) {
841)
int
l_x;
842)
843)
l_x = l_draw_no - drawNoStat[ num_sub].lastDrawNo;
844)
845)
if (l_x == 1)
846)
{
847)
drawNoStat[ num_sub].currSeq++;
848)
if (drawNoStat[ num_sub].currSeq > drawNoStat
[ num_sub].longestSeq)
849)
{
850)
drawNoStat[ num_sub].longestSeq = drawNoStat
[ num_sub].currSeq;
851)
}
852)
}
853)
else {
854)
drawNoStat[ num_sub].currSeq = 0;
855)
if (l_x > drawNoStat[num_sub].maxBtwn)
856)
{
857)
drawNoStat[ num_sub].maxBtwn = l_x;
858)
}
859)
} // end test for sequence
860)
861)
drawNoStat[ num_sub].hitCount++;
862)
drawNoStat[ num_sub].lastDrawNo = l_draw_no;
863)
drawNoStat[ num_sub].sinceLast = l_x;
864)
} // end updaterawStats method
865)
866)
private void updateMegaStats( int num_sub) {
867)
int
l_x;
868)
869)
l_x = l_draw_no - megaNoStat[ num_sub].lastDrawNo;
870)
871)
if (l_x == 1)
872)
{
873)
megaNoStat[ num_sub].currSeq++;
874)
if (megaNoStat[ num_sub].currSeq > megaNoStat
[ num_sub].longestSeq)
875)
{
876)
megaNoStat[ num_sub].longestSeq = megaNoStat
[ num_sub].currSeq;
877)
}
878)
}
879)
else {
880)
megaNoStat[ num_sub].currSeq = 0;

Chapter2MegaZillionaireApplication

144
881)
882)
883)
884)
885)
886)
887)
888)
889)
890)
891)
892)
893)
894)
895)
896)
897)
898)
899)
900)
901)
902)
903)
904)
905)
906)
907)
908)
909)
910)
911)
912)
913)
914)
915)
916)
917)
918)
919)
920)
921)
922)
923)
924)
925)
926)
927)
928)
929)
930)
931)
932)
933)
934)
935)
936)
937)
938)
939)
940)
941)
942)
943)

if (l_x > megaNoStat[num_sub].maxBtwn)


{
megaNoStat[ num_sub].maxBtwn = l_x;
}
// end test for sequence

megaNoStat[ num_sub].hitCount++;
megaNoStat[ num_sub].lastDrawNo = l_draw_no;
megaNoStat[ num_sub].sinceLast = l_x;
// end updateMegaStats method

private boolean is_record_valid() {


if (no1.getValue() == null) {
errorMsg = "No 1 Invalid";
no1.requestFocus();
return false;
}
if (no2.getValue() == null) {
errorMsg = "No 2 Invalid";
no2.requestFocus();
return false;
}
if (no3.getValue() == null) {
errorMsg = "No 3 Invalid";
no3.requestFocus();
return false;
}
if (no4.getValue() == null) {
errorMsg = "No 4 Invalid";
no4.requestFocus();
return false;
}
if (no5.getValue() == null) {
errorMsg = "No 5 Invalid";
no5.requestFocus();
return false;
}
if ( megaNo.getValue() == null) {
errorMsg = "Mega No Invalid";
megaNo.requestFocus();
return false;
}
}

return true;
// end is_record_valid method

void display_error_msg( String msg) {


JRootPane m = (JRootPane)
SwingUtilities.getAncestorOfClass( JRootPane.class, drawingDate);
if ( m != null)
{
JOptionPane.showMessageDialog(m, msg, "Entry",
JOptionPane.ERROR_MESSAGE);
}
else
System.out.println( "m was null msg was |" + msg + "|");
} // end display_error_msg

Chapter2MegaZillionaireApplication
944)
945)
946)
947)
948)
949)
950)
951)
952)
953)
954)
955)
956)
957)
958)
959)
960)
961)
962)
963)
964)
965)
966)
967)
968)
969)
970)
971)
972)
973)
974)
975)
976)
977)
978)

145

/*;;;;;
* method to find first record
*;;;;;
*/
private boolean firstRecord() {
int
l_x=0;
boolean retVal=false;
String find_str;
String localDateStr=null;
try {
megaDBF.open_database();
megaDBF.getDBF().startTop();
megaDBF.getDBF().findNext();

());

dDrawingDate
lNo1
lNo2
lNo3
lNo4
lNo5
lMegaNo

=
=
=
=
=
=
=

out_format.parse(
Integer.parseInt(
Integer.parseInt(
Integer.parseInt(
Integer.parseInt(
Integer.parseInt(
Integer.parseInt(

megaDBF.Draw_Dt.get());
megaDBF.No_1.get().trim());
megaDBF.No_2.get().trim());
megaDBF.No_3.get().trim());
megaDBF.No_4.get().trim());
megaDBF.No_5.get().trim());
megaDBF.Mega_No.get().trim

if (megaDBF.getDBF().deleted())
deletedFlg.setText("*");
else
deletedFlg.setText(" ");
megaDBF.close_database();

// Update the screen


//
drawingDate.setValue( new Integer(out_format.format
(dDrawingDate)));
979)
draw_dt_str = out_format.format( dDrawingDate);
980)
no1.setValue( new Integer(lNo1));
981)
no2.setValue( new Integer(lNo2));
982)
no3.setValue( new Integer(lNo3));
983)
no4.setValue( new Integer(lNo4));
984)
no5.setValue( new Integer(lNo5));
985)
megaNo.setValue( new Integer(lMegaNo));
986)
retVal = true;
987)
} catch( xBaseJException s) {
988)
display_error_msg( "Unable to find " + localDateStr +
989)
"Error was " + s.toString());
990)
draw_dt_str = " ";
991)
lNo1 = lNo2 = lNo3 = lNo4 = lNo5 = lMegaNo = 0;
992)
no1.setValue( null);
993)
no2.setValue( null);
994)
no3.setValue( null);
995)
no4.setValue( null);
996)
no5.setValue( null);
997)
megaNo.setValue( null);
998)
deletedFlg.setText(" ");
999)
drawingDate.setValue( null);
1000)
} catch( IOException i) {
1001)
display_error_msg( "Unable to find " + localDateStr +
1002)
"Error was " + i.toString());
1003)
draw_dt_str = " ";
1004)
lNo1 = lNo2 = lNo3 = lNo4 = lNo5 = lMegaNo = 0;

146

Chapter2MegaZillionaireApplication

1005)
no1.setValue( null);
1006)
no2.setValue( null);
1007)
no3.setValue( null);
1008)
no4.setValue( null);
1009)
no5.setValue( null);
1010)
megaNo.setValue( null);
1011)
deletedFlg.setText(" ");
1012)
drawingDate.setValue( null);
1013)
} catch( ParseException p) {
1014)
display_error_msg( "Error parsing date " + draw_dt_str +
1015)
"Error was " + p.toString());
1016)
draw_dt_str = " ";
1017)
lNo1 = lNo2 = lNo3 = lNo4 = lNo5 = lMegaNo = 0;
1018)
no1.setValue( null);
1019)
no2.setValue( null);
1020)
no3.setValue( null);
1021)
no4.setValue( null);
1022)
no5.setValue( null);
1023)
megaNo.setValue( null);
1024)
deletedFlg.setText(" ");
1025)
drawingDate.setValue( null);
1026)
} catch( NumberFormatException n) {
1027)
display_error_msg( "Error parsing date " + draw_dt_str +
1028)
"Error was " + n.toString());
1029)
draw_dt_str = " ";
1030)
lNo1 = lNo2 = lNo3 = lNo4 = lNo5 = lMegaNo = 0;
1031)
no1.setValue( null);
1032)
no2.setValue( null);
1033)
no3.setValue( null);
1034)
no4.setValue( null);
1035)
no5.setValue( null);
1036)
megaNo.setValue( null);
1037)
deletedFlg.setText(" ");
1038)
drawingDate.setValue( null);
1039)
}
1040)
1041)
return retVal;
1042)
} // end firstRecord method
1043)
1044)
/*;;;;;
1045)
* method to find previous record based upon drawing date
1046)
*;;;;;
1047)
*/
1048)
private boolean prevRecord() {
1049)
int
l_x=0;
1050)
boolean retVal=false;
1051)
String find_str;
1052)
String localDateStr=null;
1053)
1054)
if (currMode != FIND_MODE) {
1055)
display_error_msg( "Must have previously found to move back one
record");
1056)
return false;
1057)
}
1058)
1059)
draw_dt_str = drawingDate.getText();
1060)
pad_draw_dt_str();
1061)
1062)
try {
1063)
localDateStr = out_format.format( out_format.parse
(draw_dt_str));
1064)
}
1065)
catch( ParseException p) {

Chapter2MegaZillionaireApplication

147

1066)
display_error_msg( "Error parsing date " + p.toString());
1067)
localDateStr = null;
1068)
}
1069)
1070)
if (localDateStr == null)
1071)
return false;
1072)
1073)
try {
1074)
megaDBF.open_database();
1075)
megaDBF.find_EQ_record( draw_dt_str);
1076)
megaDBF.getDBF().findPrev();
1077)
1078)
dDrawingDate
= out_format.parse( megaDBF.Draw_Dt.get());
1079)
lNo1
= Integer.parseInt( megaDBF.No_1.get().trim());
1080)
lNo2
= Integer.parseInt( megaDBF.No_2.get().trim());
1081)
lNo3
= Integer.parseInt( megaDBF.No_3.get().trim());
1082)
lNo4
= Integer.parseInt( megaDBF.No_4.get().trim());
1083)
lNo5
= Integer.parseInt( megaDBF.No_5.get().trim());
1084)
lMegaNo
= Integer.parseInt( megaDBF.Mega_No.get().trim
());
1085)
1086)
if (megaDBF.getDBF().deleted())
1087)
deletedFlg.setText( "*");
1088)
else
1089)
deletedFlg.setText(" ");
1090)
1091)
megaDBF.close_database();
1092)
1093)
// Update the screen
1094)
//
1095)
drawingDate.setValue( new Integer(out_format.format
(dDrawingDate)));
1096)
draw_dt_str = out_format.format( dDrawingDate);
1097)
no1.setValue( new Integer(lNo1));
1098)
no2.setValue( new Integer(lNo2));
1099)
no3.setValue( new Integer(lNo3));
1100)
no4.setValue( new Integer(lNo4));
1101)
no5.setValue( new Integer(lNo5));
1102)
megaNo.setValue( new Integer(lMegaNo));
1103)
retVal = true;
1104)
} catch( xBaseJException s) {
1105)
display_error_msg( "Unable to find " + localDateStr +
1106)
"Error was " + s.toString());
1107)
draw_dt_str = " ";
1108)
lNo1 = lNo2 = lNo3 = lNo4 = lNo5 = lMegaNo = 0;
1109)
no1.setValue( null);
1110)
no2.setValue( null);
1111)
no3.setValue( null);
1112)
no4.setValue( null);
1113)
no5.setValue( null);
1114)
megaNo.setValue( null);
1115)
drawingDate.setValue( null);
1116)
deletedFlg.setText(" ");
1117)
} catch( IOException i) {
1118)
display_error_msg( "Unable to find " + localDateStr +
1119)
"Error was " + i.toString());
1120)
draw_dt_str = " ";
1121)
lNo1 = lNo2 = lNo3 = lNo4 = lNo5 = lMegaNo = 0;
1122)
no1.setValue( null);
1123)
no2.setValue( null);
1124)
no3.setValue( null);
1125)
no4.setValue( null);
1126)
no5.setValue( null);

148

Chapter2MegaZillionaireApplication

1127)
megaNo.setValue( null);
1128)
drawingDate.setValue( null);
1129)
deletedFlg.setText(" ");
1130)
} catch( ParseException p) {
1131)
display_error_msg( "Error parsing date " + draw_dt_str +
1132)
"Error was " + p.toString());
1133)
draw_dt_str = " ";
1134)
lNo1 = lNo2 = lNo3 = lNo4 = lNo5 = lMegaNo = 0;
1135)
no1.setValue( null);
1136)
no2.setValue( null);
1137)
no3.setValue( null);
1138)
no4.setValue( null);
1139)
no5.setValue( null);
1140)
megaNo.setValue( null);
1141)
drawingDate.setValue( null);
1142)
deletedFlg.setText(" ");
1143)
} catch( NumberFormatException n) {
1144)
display_error_msg( "Error parsing date " + draw_dt_str +
1145)
"Error was " + n.toString());
1146)
draw_dt_str = " ";
1147)
lNo1 = lNo2 = lNo3 = lNo4 = lNo5 = lMegaNo = 0;
1148)
no1.setValue( null);
1149)
no2.setValue( null);
1150)
no3.setValue( null);
1151)
no4.setValue( null);
1152)
no5.setValue( null);
1153)
megaNo.setValue( null);
1154)
drawingDate.setValue( null);
1155)
deletedFlg.setText(" ");
1156)
}
1157)
1158)
return retVal;
1159)
} // end prevRecord method
1160)
1161)
/*;;;;;
1162)
* method to find a next record based upon drawing date
1163)
*;;;;;
1164)
*/
1165)
private boolean nextRecord() {
1166)
int
l_x=0;
1167)
boolean retVal=false;
1168)
String find_str;
1169)
String localDateStr=null;
1170)
1171)
draw_dt_str = drawingDate.getText();
1172)
pad_draw_dt_str();
1173)
1174)
try {
1175)
localDateStr = out_format.format( out_format.parse
(draw_dt_str));
1176)
}
1177)
catch( ParseException p) {
1178)
display_error_msg( "Error parsing date " + p.toString());
1179)
localDateStr = null;
1180)
}
1181)
1182)
if (localDateStr == null)
1183)
return false;
1184)
1185)
try {
1186)
megaDBF.open_database();
1187)
megaDBF.find_GE_record( localDateStr.trim());
1188)
megaDBF.getDBF().findNext();

Chapter2MegaZillionaireApplication

149

1189)
1190)
dDrawingDate
= out_format.parse( megaDBF.Draw_Dt.get());
1191)
lNo1
= Integer.parseInt( megaDBF.No_1.get().trim());
1192)
lNo2
= Integer.parseInt( megaDBF.No_2.get().trim());
1193)
lNo3
= Integer.parseInt( megaDBF.No_3.get().trim());
1194)
lNo4
= Integer.parseInt( megaDBF.No_4.get().trim());
1195)
lNo5
= Integer.parseInt( megaDBF.No_5.get().trim());
1196)
lMegaNo
= Integer.parseInt( megaDBF.Mega_No.get().trim
());
1197)
if (megaDBF.getDBF().deleted())
1198)
deletedFlg.setText("*");
1199)
else
1200)
deletedFlg.setText(" ");
1201)
1202)
megaDBF.close_database();
1203)
1204)
// Update the screen
1205)
//
1206)
drawingDate.setValue( new Integer(out_format.format
(dDrawingDate)));
1207)
draw_dt_str = out_format.format( dDrawingDate);
1208)
no1.setValue( new Integer(lNo1));
1209)
no2.setValue( new Integer(lNo2));
1210)
no3.setValue( new Integer(lNo3));
1211)
no4.setValue( new Integer(lNo4));
1212)
no5.setValue( new Integer(lNo5));
1213)
megaNo.setValue( new Integer(lMegaNo));
1214)
retVal = true;
1215)
} catch( xBaseJException s) {
1216)
display_error_msg( "Unable to find " + localDateStr +
1217)
"Error was " + s.toString());
1218)
draw_dt_str = " ";
1219)
lNo1 = lNo2 = lNo3 = lNo4 = lNo5 = lMegaNo = 0;
1220)
no1.setValue( null);
1221)
no2.setValue( null);
1222)
no3.setValue( null);
1223)
no4.setValue( null);
1224)
no5.setValue( null);
1225)
megaNo.setValue( null);
1226)
drawingDate.setValue( null);
1227)
} catch( IOException i) {
1228)
display_error_msg( "Unable to find " + localDateStr +
1229)
"Error was " + i.toString());
1230)
draw_dt_str = " ";
1231)
lNo1 = lNo2 = lNo3 = lNo4 = lNo5 = lMegaNo = 0;
1232)
no1.setValue( null);
1233)
no2.setValue( null);
1234)
no3.setValue( null);
1235)
no4.setValue( null);
1236)
no5.setValue( null);
1237)
megaNo.setValue( null);
1238)
drawingDate.setValue( null);
1239)
} catch( ParseException p) {
1240)
display_error_msg( "Error parsing date " + draw_dt_str +
1241)
"Error was " + p.toString());
1242)
draw_dt_str = " ";
1243)
lNo1 = lNo2 = lNo3 = lNo4 = lNo5 = lMegaNo = 0;
1244)
no1.setValue( null);
1245)
no2.setValue( null);
1246)
no3.setValue( null);
1247)
no4.setValue( null);
1248)
no5.setValue( null);
1249)
megaNo.setValue( null);

150

Chapter2MegaZillionaireApplication

1250)
drawingDate.setValue( null);
1251)
}
1252)
1253)
return retVal;
1254)
} // end nextRecord method
1255)
1256)
/*;;;;;
1257)
* method to find last record based upon drawing date
1258)
*;;;;;
1259)
*/
1260)
private boolean lastRecord() {
1261)
int
l_x=0;
1262)
boolean retVal=false;
1263)
String find_str;
1264)
String localDateStr=null;
1265)
1266)
try {
1267)
megaDBF.open_database();
1268)
megaDBF.getDBF().startBottom();
1269)
megaDBF.getDBF().findPrev();
1270)
1271)
dDrawingDate
= out_format.parse( megaDBF.Draw_Dt.get());
1272)
lNo1
= Integer.parseInt( megaDBF.No_1.get().trim());
1273)
lNo2
= Integer.parseInt( megaDBF.No_2.get().trim());
1274)
lNo3
= Integer.parseInt( megaDBF.No_3.get().trim());
1275)
lNo4
= Integer.parseInt( megaDBF.No_4.get().trim());
1276)
lNo5
= Integer.parseInt( megaDBF.No_5.get().trim());
1277)
lMegaNo
= Integer.parseInt( megaDBF.Mega_No.get().trim
());
1278)
1279)
if (megaDBF.getDBF().deleted())
1280)
deletedFlg.setText("*");
1281)
else
1282)
deletedFlg.setText(" ");
1283)
1284)
megaDBF.close_database();
1285)
1286)
// Update the screen
1287)
//
1288)
drawingDate.setValue( new Integer(out_format.format
(dDrawingDate)));
1289)
draw_dt_str = out_format.format( dDrawingDate);
1290)
no1.setValue( new Integer(lNo1));
1291)
no2.setValue( new Integer(lNo2));
1292)
no3.setValue( new Integer(lNo3));
1293)
no4.setValue( new Integer(lNo4));
1294)
no5.setValue( new Integer(lNo5));
1295)
megaNo.setValue( new Integer(lMegaNo));
1296)
retVal = true;
1297)
} catch( xBaseJException s) {
1298)
display_error_msg( "Unable to find " + localDateStr +
1299)
"Error was " + s.toString());
1300)
draw_dt_str = " ";
1301)
lNo1 = lNo2 = lNo3 = lNo4 = lNo5 = lMegaNo = 0;
1302)
no1.setValue( null);
1303)
no2.setValue( null);
1304)
no3.setValue( null);
1305)
no4.setValue( null);
1306)
no5.setValue( null);
1307)
megaNo.setValue( null);
1308)
drawingDate.setValue( null);
1309)
} catch( IOException i) {
1310)
display_error_msg( "Unable to find " + localDateStr +

Chapter2MegaZillionaireApplication
1311)
1312)
1313)
1314)
1315)
1316)
1317)
1318)
1319)
1320)
1321)
1322)
1323)
1324)
1325)
1326)
1327)
1328)
1329)
1330)
1331)
1332)
1333)
1334)
1335)
1336)
1337)
1338)
1339) }

151

"Error was " + i.toString());


draw_dt_str = " ";
lNo1 = lNo2 = lNo3 = lNo4 = lNo5 = lMegaNo = 0;
no1.setValue( null);
no2.setValue( null);
no3.setValue( null);
no4.setValue( null);
no5.setValue( null);
megaNo.setValue( null);
drawingDate.setValue( null);
} catch( ParseException p) {
display_error_msg( "Error parsing date " + draw_dt_str +
"Error was " + p.toString());
draw_dt_str = " ";
lNo1 = lNo2 = lNo3 = lNo4 = lNo5 = lMegaNo = 0;
no1.setValue( null);
no2.setValue( null);
no3.setValue( null);
no4.setValue( null);
no5.setValue( null);
megaNo.setValue( null);
drawingDate.setValue( null);
}
}

return retVal;
// end findRecord method

// end MegaXbaseEntryPanel class definition

I'm
sorry.ThelistingforMegaXbaseEntryPanelisjustlong.Itwouldhavebeenevenlonger
ifIhadcontinuedaddingsomeotherneatfeatures.
Weneedtostartourdiscussionatlistinglines79through102.Ihavenowayofknowing
whatlevelyourJavaskillsare,soletmepointouttheinternalclasscontraptionweusetoverify
drawingnumberinputiswithinavalidrange.Everythingbetweenthefirst{andtheending}
becomes the body of the abstract class. We must provide a Boolean verify() method and a
Boolean shouldYieldFocus() method. The actual editing/validating happens in the verify()
method.HereweconverttheJComponentobjecttoatypeweknowittobe.(Yes,Icouldhave
madethisevenmorerobustbycheckingwithinstanceof.)Wepassbackeithertrueorfalseto
indicatethequalityofthedata.
YouwillfinddocumentationonlinewhichclaimstheshouldYieldFocus()methodisoptional.
Iwouldn't
buythatstatementeveniftheypaidmetotakeit,mainlybecauseyearsagoItriedto
leaveitout.Wearen't
farenoughintothesourcecodeyet,butitshouldn't
takemuchforyouto
believethatIliketotossuperrormessagedialogswhensomethingfailedvalidation.Afterthe
firsterrortriestothrowadialogupyoufindyourselfinadeadlyembracewithmultiplethings
absolutelydemandingtheybeallowedtohavefocusatthesametime.It's
notaprettypicture.
Dependingonhowyoulaunchedyourapplicationyoucouldevenbeforcedtoreboot.Youdon't
directlycallshouldYieldFocus(),theunderlyingSwingAPIdoes.

152

Chapter2MegaZillionaireApplication

Pleaselookatlistinglines254through259. Thisiswherewecreatethetop ofdata or


first record button(<<<<).TheLINE_STARTvalueisbasicallyasynonymforWEST.The
reallyimportantlineiswherewesetgbc.weightxto1.0.Thiscreatesthegapbetweenthe(<<<<)
buttonandthe(<)button. Ididnotspendmuchtimeclosingthegapbetweenthe(<)and(>)
buttons.Thisgapexistsfortworeasons:
1. buttonsaresizedbaseduponthetexttheycontain
2. buttons are positioned based upon the order in which they are declared and
gridwidth
Youcanchangethegridwidthforthe(>)buttonto1fromRELATIVEandyoucanaddboth
aleadingandtrailing spaceinthetextofbothbuttons. Thiswillcausethebuttonstomush
togetherunderFindandDelete,buttheywon't
reallybevisuallycentered.GridBagConstraints
does have a public integer array named columnWidths and it provides a method
getLayoutDimensions()columnwidthsandrowheightsforthecurrentlayout.Ifyouputmore
columnWidthvaluesintothearraythancurrentlyexist,columnswillbeaddedtothelayout. I
leavethecenteringofthosebuttonsasanexerciseforthereader.
Listingline294issomethingIhaven't
talkedmuchabout;I've
justbeenusingit.Whenyou
getdonecreatingapanelordialogyoucantellSwingwhatcomponentyouwanttohavefocusby
callingthismethod.Besuretorememberthatcallingitintheconstructormeansthefocusisonly
setthefirsttimetheobjectisdisplayed.
Our actionPerformed() method at listing line 311 would really benefit if Java could add
switchconstructswhichusednonconstantvaluesinthecasestatements.Thelogicwouldatleast
lookalotcleaner.
NormallyIwouldn'tputabunchofassignmentstatementsinaneventswitch as Ididat
listinglines348through358,butIgotcutandpastehappy,andIwantedtogiveyouaneasy
assignment.Eventuallyyouwillbecreatingaprivatemethodtoreplaceallofthesecodeblocks.
NoticethatIcallthedisplay_error_msg()methodfrommanyplacesinthecode.Ifyoutake
alookatlistingline933youwillseethattinysnippetofcodeItalkedaboutinthelastmodule
wasplacedintoitsownmethodwhereitcouldservemanypurposes.
Listingline402containsacalltopad_draw_dt_str().Ihadtocreatethatmethodtoallowfor
partialdateentry.Iprobablyshouldn't
havecalleditintheaddRecord()methodbecauseitwill
allowforausertoenterapartialdatewhenaddingarecord.Themethodwillusethedatestring
19990101whenitislookingtofillinmissingdatecomponents.

Chapter2MegaZillionaireApplication

153

Thesectionofcodeatlistinglines493through504containsaseriousmultiuserproblem.I
dutifullyfoundtherecordtoupdate,movedinthevalues,andwroteitbacktothefile.WhatI
didn'tdowaschecktosee
whetheranyotheruserorprocesshadchangedthevaluesofthatrecord
betweenthetimetheentryscreenloadeditandtheuserchosetosaveit.
Wedon't
haveanythinginterestingtotalkaboutuntilwegetdowntolistinglines762and
763.Thisisthefirst,andonly,timeIusetheStatDBFclasswecreated.NoticehowIpasseda
short name without extensionto thecreate_database() method. Ido not know ifthe xBaseJ
libraryhasafilenamelengthlimitation,butitwas8.3atonepointduringthedaysofDOS.Since
Iwillbeaddingk 0 totheendofthenametocreatetheNDXfiles,Ioptedtopassonly6
charactersin. IalsoallowJavagarbage collectiontocloseoffthesefilessometimeafterthe
objectsgooutofscope.
Listing line 832 contains another version of that message dialog. This time we pass
PLAIN_MESSAGE as the option so Swing displays a runofthemill status message dialog
insteadofanerrormessage.
Listinglines969through972containanifstatementwhichisreplicatedinafewplaces.Here
wecallthedeleted()methodprovidedbytheDBFclasstodetermineifwedisplayaspaceoran
*intheDeletedfieldonthescreen.
Noticethateachfind methodinthisclassstoresthevaluesfoundinclassglobalfields.You
needtopayattention tothatdesignfeatureifyouintendtocompleteoneoftheassignments
comingupattheendofthechapter.
Listinglines978and979mayrequireatinybitofexplanation.Theobjectwhichallowsfor
dateentryonourpanelisasimpleJformattedTextField.Ichosenottoplaygamestryingtomake
thisdisplaywithaprettyformat.It's
notthatIdon't
likeprettydateformats,it's
justthatIdidn't
wanttoinstallandincludetheapachelibrariestogettheirJdateFieldobjectandIdidn't
wantto
complicatetheentrysequencebycreatingaJDateChooserobject.Theendresultisthatwehave
toconvertthedatefromthedatabasefromstringtoadateobjectJavalikes,thenformatittoa
newstringtoconverttoanIntegerobject.Theconversionfromstringintoadatedatatypehelps
validatethecolumnonthedatabase.Wecouldchoosetotrustit,butwhybotherwhenitdoesn't
costthatmanycyclestobesure?
Thereyouhaveit:ThebiggestsourcefileI'v
eshownyousofarinthisbook.Perhapsyou
noticed we talked more about the Java aspects of this module than the xBaseJ aspects. You
alreadyhavemostofthefundamentalsdown.Thepurposeofthischapteristogiveyouideason
howtoboltthemtogethercorrectly,atleastfromadesignstandpoint.

Chapter2MegaZillionaireApplication

154
2.4 TheImportDialog

Theimportdialogisgoingtoseemreallylameafterthemainportionofthisapplication.I
chosetomaketheimportmoduleadialogbefore Istartedwritingtherestoftheapplication.
Therewasactuallyamethodtothemadness.IftheImportfunctionwassimplyanotherpanel,it
wouldbepossibleforausertochooseafilename,thenleavethescreenbyselectinganother
menuoption.Heorshewouldnothaveactuallyperformedtheimport,butmightbelieveitwas
complete.Makingthisadialogstoppedthatfromhappening.
MegaXImport.java
1)
2)
3)
4)
5)
6)
7)
8)
9)
10)
11)
12)
13)
14)
15)
16)
17)
18)
19)
20)
21)
22)
23)
24)
25)
26)
27)
28)
29)
30)
31)
32)
33)
34)
35)
36)
37)
38)
39)
40)
41)
42)
43)
44)
45)
46)
47)

package com.logikal.megazillxBaseJ;
import
import
import
import
import
import
import

java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.filechooser.*;
java.text.*;
java.util.*;
java.io.*;

import
import
import
import

org.xBaseJ.*;
org.xBaseJ.fields.*;
org.xBaseJ.Util.*;
org.xBaseJ.indexes.NDX;

import com.logikal.megazillxBaseJ.MegaDBF;
public class MegaXImport extends JDialog implements ActionListener{
// Variables
public JButton
importData=null;
public JButton
exitButton=null;
public JButton
chooseFile=null;
private JTextField
csvNameField=null;
private JPanel
line1Panel=null;
private JPanel
line2Panel=null;
private MegaDBF
MegaDB = null;
private String
CsvName=null;
private JTextArea
importRptArea;
private JScrollPane
sp;
private JFileChooser
fc;
// constructor
public MegaXImport(Frame owner) {
super( owner, "Import CSV", true);
setSize( 800, 500);
setLayout( new FlowLayout());

// constructor for parent class

line1Panel = new JPanel( new FlowLayout( FlowLayout.LEFT));


JLabel csvNameLabel = new JLabel("CSV File:");
line1Panel.add(csvNameLabel);
csvNameField = new JTextField(40);
csvNameField.setEditable( false);
line1Panel.add(csvNameField);

Chapter2MegaZillionaireApplication
48)
49)
50)
51)
52)
53)
54)
55)
56)
57)
58)
59)
60)
61)
62)
63)
64)
65)
66)
67)
68)
69)
70)
71)
72)
73)
74)
75)
76)
77)
78)
79)
80)
81)
82)
83)
84)
85)
86)
87)
88)
89)
90)
91)
92)
93)
94)
95)
96)
97)
98)
99)
100)
101)
102)
103)
104)
105)
106)
107)
108)
109)
110)

line2Panel = new JPanel( new FlowLayout( FlowLayout.LEFT));


importRptArea = new JTextArea();
// Gives you a fixed width font.
importRptArea.setFont(new Font("Courier", Font.PLAIN, 12));
importRptArea.setEditable( false);
importRptArea.setTabSize(4);
importRptArea.setColumns( 80);
importRptArea.setRows(1000);
importRptArea.setDoubleBuffered( true);
sp = new JScrollPane( importRptArea);
sp.setPreferredSize( new Dimension( 500, 300));
line2Panel.add(sp);
chooseFile = new JButton();
chooseFile.setText("Choose File");
chooseFile.addActionListener( this);
line1Panel.add( chooseFile);
importData = new JButton();
importData.setText("OK");
importData.addActionListener( this);
line2Panel.add(importData);
exitButton = new JButton();
exitButton.setText( "Exit");
exitButton.addActionListener( this);
line2Panel.add(exitButton);
add( line1Panel);
add( line2Panel);
chooseFile.requestFocus();
getRootPane().setDefaultButton( importData);
setLocationRelativeTo(owner);
MegaDB = new MegaDBF();
}

// end constructor for RdbLogin

//;;;;;
// Obtain file name and kick off import process
//;;;;;
public void actionPerformed(ActionEvent event) {
String actionStr = event.getActionCommand();
if (actionStr.indexOf( "Exit") > -1) {
this.setVisible( false);
return;
}
if (actionStr.indexOf( "Choose") > -1) {
fc = new JFileChooser();
int ret_val = fc.showOpenDialog(this);
if (ret_val == JFileChooser.APPROVE_OPTION) {
File f = fc.getSelectedFile();
csvNameField.setText( f.getAbsolutePath());
}
} // end test for choose actionStr

155

Chapter2MegaZillionaireApplication

156
111)
112)
113)
114)
115)
116)
117)
118)
119)
120)
121)
122)
123)
124)
125)
126)
127)
128)
129)
130)
131)
132)
133)
134)
135)
136)
137)
138)
139)
140)
141)
142)
143)
144)
145)
146)
147)
148)
149)
150)
151)
152)
153)
154)
155)
156)
157)
158)
159)
160)
161)
162)
163)
164)
165)
166)
167)
168)
169)
170)
171)
172)
173)

if (actionStr.indexOf( "OK") > -1) {


if (!importCSV(csvNameField.getText() )) {
importRptArea.append( "Import not successfull\n");
importRptArea.append( "please try again\n");
}
}
// end actionPerformed

//;;;;;
// Actual import logic happens here
//;;;;;
private boolean importCSV( String the_file) {
String line_in_str = null;
long l_record_count = 0;
boolean eof_flg = false, ret_val = false;
FileReader in_file = null;
BufferedReader input_file = null;
importRptArea.append( "\nAttempting to import " + the_file + "\n");
updateText();
try {
in_file = new FileReader( the_file);
} catch (FileNotFoundException f) {
importRptArea.append("File Not Found " + the_file);
eof_flg = true;
} // end catch for file not found
if (eof_flg == true)
return ret_val;
MegaDB.create_database();
input_file = new BufferedReader( in_file,4096);
importRptArea.append("\nPopulating database\n");
updateText();
while (eof_flg == false) {
try {
line_in_str = input_file.readLine();
}
catch (EOFException e) {
importRptArea.append( "End of file exception\n");
eof_flg = true;
}
catch (IOException e) {
importRptArea.append( "An IO Exception occurred\n");
importRptArea.append( e.toString());
//e.printStackTrace();
eof_flg = true;
}
if (eof_flg == true)
continue;
if (line_in_str == null) {
importRptArea.append( "End of intput file reached\n");
eof_flg = true;
continue;
}
l_record_count++;
String input_flds[] = line_in_str.split( ",");
try {

Chapter2MegaZillionaireApplication
174)
175)
176)
177)
178)
179)
180)
181)
182)

157

MegaDB.No_1.put( input_flds[1]);
MegaDB.No_2.put( input_flds[2]);
MegaDB.No_3.put( input_flds[3]);
MegaDB.No_4.put( input_flds[4]);
MegaDB.No_5.put( input_flds[5]);
MegaDB.Mega_No.put( input_flds[6]);
String date_parts[] = input_flds[0].split("-");
String dt_str = date_parts[0] + date_parts[1] + date_parts

[2];
183)
MegaDB.Draw_Dt.put( dt_str);
184)
185)
MegaDB.getDBF().write();
186)
187)
} catch ( xBaseJException j){
188)
j.printStackTrace();
189)
} catch( IOException i) {
190)
importRptArea.append( i.getMessage());
191)
}
192)
193)
if ( (l_record_count % 100) == 0) {
194)
importRptArea.append( "Processed " + l_record_count +
195)
" records\n");
196)
updateText();
197)
} // end of test for status message
198)
199)
} // end while loop to load records
200)
201)
importRptArea.append( "Finished adding " + l_record_count +
202)
" records\n");
203)
204)
return true;
205)
206)
} // end importCSV method
207)
208)
public void updateText() {
209)
importRptArea.invalidate();
210)
importRptArea.validate();
211)
importRptArea.paintImmediately( importRptArea.getVisibleRect());
212)
}
213) } // end MegaXImport class

Listinglines24and25areworthyofnote. Someofyoumayhavetheimpressionthata
panelisascreen.Asourconstructorshows,thisissimplynotthecase.Weallocateonepanelto
containthetheCSVfilenameprompt,textfield,andtheChoosebutton. Asecondpanelis
createdtocontainthetextareaandscrollpanealongwiththeOkandExitbuttons.Whenyouare
usingtheFlowLayoutinsteadoftheGridLayoutitisquitecommontohavemultiplepanelsina
containingobject.Itprovidesamethodofcontrollingtheflowbygroupingobjectstogether.
Noticelistinglines81through84.Afterwehaveaddedthepanelstothedialog,wehavethe
buttontochooseafilerequestfocusbutwesetthedefaultbuttontobetheimportbutton.Ifyou
havetriedrunningtheapplicationyouwillalreadyhavelearnedthe lastoneinwon. Thetext
entryfieldisthefieldwhichactuallyhasfocus,buttheOkbuttonishighlightedtoindicatethat
hittingreturnwillactivateit.

Chapter2MegaZillionaireApplication

158

Listingline104iswhatensurestheusermustcompletethefilechooserdialogonewayor
anotherbeforethisdialogcontinues.Ihopeyoudon't
finditstrangethatadialogcanthrowupa
dialogwhichcanthrowupanotherdialogthatcanthrowupanotherdialog.Ihaven't
conducteda
testtoseejusthowdeepyoucango,butIassumeithassomethingtodowitha2GBmemory
limitimposedonmanyJVMinstalls.
Thedialogreturnsanintegervaluetoinformusofitscompletionstatus.Iftheuserchose
andapprovedafilename,wecallgetSelectedFile()toobtaintheFileobject.Wethenhavetocall
getAbsolutePath()toobtainthefullpathname.Undermostcircumstances,youcannotopenthe
fileunlessyouprovidethefullpathname.Ididn't
provideastartinglocationforthefilechooser
soitwillstartintheuser's
homedirectoryinsteadofthecurrentworkingdirectory.Ifyouwantit
tostarttheresimplychangelistingline103toreadasfollows:
fc = new JfileChooser(System.getProperty("user.dir"));

There isn'tmuch left to discuss in the importCSV() method. You know that I call the
updateText()methodtoforcescreenupdatessomystatusmessagesgetdisplayedwhiletheyare
relevantinsteadofafterthetaskcompletes.Ihavealreadyprovidedyouseveralexampleswhich
readalineofinputfromatextfileandusetheStringsplit()methodtobreakitintoseparatedata
items. WehaveusedtheFieldput()methodandtheDBFwrite()methodmanytimesoverin
previoussourcelistings.
MegaXbase.java
1)
2)
3)
4)
5)
6)
7)
8)
9)
10)
11)
12)
13)
14)
15)
16)
17)
18)
19)
20)
21)
22)
23)
24)
25)
26)
27)
28)

import
import
import
import

java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.plaf.*;

import com.sun.java.swing.plaf.windows.WindowsLookAndFeel;
import com.sun.java.swing.plaf.gtk.GTKLookAndFeel;
import com.sun.java.swing.plaf.motif.MotifLookAndFeel;
import
import
import
import

com.logikal.megazillxBaseJ.MegaXImport;
com.logikal.megazillxBaseJ.MegaXbaseBrowsePanel;
com.logikal.megazillxBaseJ.MegaXbaseEntryPanel;
com.logikal.megazillxBaseJ.MegaXbaseDuePanel;

public class MegaXbase implements ActionListener, ItemListener {


private
private
private
private
private
private
private
final
final
final
final

JFrame
JPanel
JMenuBar
JPanel
MegaXbaseBrowsePanel
MegaXbaseEntryPanel
MegaXbaseDuePanel

static
static
static
static

String
String
String
String

mainFrame=null;
mainPanel=null;
mb = null;
blankPanel=null;
mxb=null;
mxe=null;
md=null;

MOSTPANEL = "Most Report";


DUEPANEL = "Due Report";
DUMPPANEL = "Dump Report";
ENTRYPANEL = "Entry";

Chapter2MegaZillionaireApplication
29)
30)
31)
32)
33)
34)
35)
36)
37)
38)
39)
40)
41)
42)
43)
44)
45)
46)
47)
48)
49)
50)
51)
52)
53)
54)
55)
56)
57)
58)
59)
60)
61)
62)
63)
64)
65)
66)
67)
68)
69)
70)
71)
72)
73)
74)
75)
76)
77)
78)
79)
80)
81)
82)
83)
84)
85)
86)
87)
88)
89)
90)
91)

159

final static String BLANKPANEL = "Blank";


final static String BROWSEPANEL = "Browse";
public MegaXbase() {
//---------// All of this code just to set the look and feel
//
int nimbus_sub = -1;
int motif_sub = -1;
int chosen_sub;
try {
// Set System Look and Feel
UIManager.LookAndFeelInfo lf[] = UIManager.getInstalledLookAndFeels();
for( int y=0; y < lf.length; y++) {
String s = lf[ y].getName();
System.out.println( s);
if ( s.indexOf( "Nimbus") > -1) {
nimbus_sub = y;
System.out.println( "\tNimbus found");
}
if ( s.indexOf( "Motif") > -1) {
motif_sub = y;
System.out.println( "\tMotif found");
}
}
if ( nimbus_sub > -1)
chosen_sub = nimbus_sub;
else if ( motif_sub > -1)
chosen_sub = motif_sub;
else chosen_sub = 0;
UIManager.setLookAndFeel( lf[chosen_sub].getClassName());
// UIManager.getSystemLookAndFeelClassName());
}
catch (UnsupportedLookAndFeelException e) {
System.out.println( "Unsupported look and feel");
}
catch (ClassNotFoundException e) {
System.out.println( "classnotfound");
}
catch (InstantiationException e) {
System.out.println( "Instantiation exeception");
}
catch (IllegalAccessException e) {
System.out.println( "Illegal Access");
}
mainFrame = new JFrame("Mega xBaseJ Window");
mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE);
mainFrame.setJMenuBar( createMenu());
mainPanel = new JPanel(new CardLayout());
mainPanel.setOpaque(true);
blankPanel = new JPanel();
mxb = new MegaXbaseBrowsePanel();
mxe = new MegaXbaseEntryPanel();
md = new MegaXbaseDuePanel();
mainPanel.add( blankPanel, BLANKPANEL);

Chapter2MegaZillionaireApplication

160
92)
93)
94)
95)
96)
97)
98)
99)
100)
101)
102)
103)
104)
105)
106)
107)
108)
109)
110)
111)
112)
113)
114)
115)
116)
117)
118)
119)
120)
121)
122)
123)
124)
125)
126)
127)
128)
129)
130)
131)
132)
133)
134)
135)
136)
137)
138)
139)
140)
141)
142)
143)
144)
145)
146)
147)
148)
149)
150)
151)
152)
153)
154)

mainPanel.add( mxb, BROWSEPANEL);


mainPanel.add( mxe, ENTRYPANEL);
mainPanel.add( md, DUEPANEL);
mainFrame.setContentPane( mainPanel);
mainFrame.setSize(800, 500);
mainFrame.setVisible( true);
}

// end default constructor

private JMenuBar createMenu() {


JMenu fileMenu, reportMenu;
JMenuItem menuItem;
mb = new JMenuBar();
//;;;;;
// Build the File menu
//;;;;;
fileMenu = new JMenu("File");
fileMenu.setMnemonic(KeyEvent.VK_F);
fileMenu.getAccessibleContext().setAccessibleDescription(
"File operation menu");
// Import menu option
menuItem = new JMenuItem("Import", KeyEvent.VK_I);
menuItem.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_I,
ActionEvent.ALT_MASK));
menuItem.getAccessibleContext().setAccessibleDescription(
"Imports data from CSV creating new DBF");
menuItem.setActionCommand("Import");
menuItem.addActionListener( this);
fileMenu.add( menuItem );
// Maintenance Menu Option
menuItem = new JMenuItem("Maintenance", KeyEvent.VK_M);
menuItem.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_M,
ActionEvent.ALT_MASK));
menuItem.getAccessibleContext().setAccessibleDescription(
"Manual Entry/Editing/Deletion");
menuItem.setActionCommand("Entry");
menuItem.addActionListener( this);
fileMenu.add( menuItem);
// Browse option
menuItem = new JMenuItem("Browse", KeyEvent.VK_B);
menuItem.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_B,
ActionEvent.ALT_MASK));
menuItem.getAccessibleContext().setAccessibleDescription(
"View Data");
menuItem.setActionCommand("Browse");
menuItem.addActionListener( this);
fileMenu.add( menuItem);
fileMenu.addSeparator();
menuItem = new JMenuItem("Exit", KeyEvent.VK_X);
menuItem.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_X,
ActionEvent.ALT_MASK));
menuItem.getAccessibleContext().setAccessibleDescription(
"Exit program");

Chapter2MegaZillionaireApplication
155)
156)
157)
158)
159)
160)
161)
162)
163)
164)
165)
166)
167)
168)
169)
170)
171)
172)
173)
174)
175)
176)
177)
178)
179)
180)
181)
182)
183)
184)
185)
186)
187)
188)
189)
190)
191)
192)
193)
194)
195)
196)
197)
198)
199)
200)
201)
202)
203)
204)
205)
206)
207)
208)
209)
210)
211)
212)
213)
214)
215)
216)
217)

menuItem.setActionCommand("Exit");
menuItem.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent evt){
System.exit(0);}});
fileMenu.add( menuItem);
//;;;;;
// Build the File menu
//;;;;;
reportMenu = new JMenu("Report");
reportMenu.setMnemonic(KeyEvent.VK_R);
reportMenu.getAccessibleContext().setAccessibleDescription(
"Report creation menu");
// Import menu option
menuItem = new JMenuItem("Complete Data Dump", KeyEvent.VK_C);
menuItem.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_C,
ActionEvent.ALT_MASK));
menuItem.getAccessibleContext().setAccessibleDescription(
"Reports all data on file");
menuItem.setActionCommand("Dump");
menuItem.addActionListener( this);
reportMenu.add( menuItem );
menuItem = new JMenuItem("Most Often Hit", KeyEvent.VK_M);
menuItem.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_M,
ActionEvent.ALT_MASK));
menuItem.getAccessibleContext().setAccessibleDescription(
"Most frequently drawn numbers");
menuItem.setActionCommand("Most");
menuItem.addActionListener( this);
reportMenu.add( menuItem );
menuItem = new JMenuItem("Due Numbers", KeyEvent.VK_D);
menuItem.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_D,
ActionEvent.ALT_MASK));
menuItem.getAccessibleContext().setAccessibleDescription(
"Due numbers");
menuItem.setActionCommand("Due");
menuItem.addActionListener( this);
reportMenu.add( menuItem );
//;;;;;
// Add the new menus to the bar
//;;;;;
mb.add( fileMenu);
mb.add( reportMenu);
}

return mb;
// end createMenu method

//;;;;;
//
When a user choses a menu item we process it here
//
If Java would allow a switch to use strings, or would give
//
the JButton class a number field which got passed to a field in
//
the ActionEvent class, this code would be a lot cleaner.
//;;;;;
public void actionPerformed( ActionEvent e) {
String actionStr = e.getActionCommand();
System.out.println( "\nSelected action " + actionStr);

161

162

Chapter2MegaZillionaireApplication

218)
Frame f = (Frame) SwingUtilities.getAncestorOfClass( Frame.class, mb);
219)
220)
if (actionStr.indexOf( "Import") > -1) {
221)
MegaXImport importDialog = new MegaXImport( f);
222)
importDialog.setVisible(true);
223)
importDialog.dispose();
224)
}
225)
else if (actionStr.indexOf("Browse") > -1) {
226)
CardLayout cl = (CardLayout)(mainPanel.getLayout());
227)
cl.show(mainPanel, BROWSEPANEL );
228)
229)
} else if (actionStr.indexOf("Entry") > -1) {
230)
CardLayout cl = (CardLayout)(mainPanel.getLayout());
231)
cl.show(mainPanel, ENTRYPANEL );
232)
233)
} else if (actionStr.indexOf("Due") > -1) {
234)
CardLayout cl = (CardLayout)(mainPanel.getLayout());
235)
cl.show(mainPanel, DUEPANEL );
236)
237)
} else {
238)
System.out.println( "unhandled action");
239)
}
240)
241)
} // end actionPerformed method
242)
243)
public void itemStateChanged( ItemEvent e) {
244)
System.out.println( "state change");
245)
System.out.println( e.toString());
246)
}
247)
248)
249)
250) } // end MegaXbase class definition

Thecodeforthemainmenuisjustatinybitconvoluted.Listinglines41through76exist
becauseIbelieveMetalisprobablytheugliestLookandFeelanyonecouldhaveinvented. I
neededtochangethatlookandfeelwithouthavingthisthingcrashthefirsttimeyou tried to
compileit.ThesafethingformetodowasscanthroughthelistofLookandFeelswhichJava
thought were installed. Until the advent of Java 1.6 and the creation of a file called
swing.properties,JavahadnorealwayoffindingoutaboutanylookandfeelSundidn'tprovide.
Traditionally,applicationswillinclude anextraJARfilecontaininga Lookand Feeland
makecertainthatJARfilegetsaddedtotheCLASSPATHenvironmentvariable.Thisallowsthe
codetochangeaLookandFeeltobemuchshorter.Simplyaddanimportlineatthetopandthen
replacethetryblockwithacleanerpieceofcode.
import com.nilo.plaf.nimrod.*;
try {
UIManager.setLookAndFeel( new com.nilo.plaf.nimrod.NimRODLookAndFeel());
}
catch (UnsupportedLookAndFeelException e) {
System.out.println( "Unsupported look and feel");
}

Chapter2MegaZillionaireApplication

163

Itshouldn't
surpriseyoutolearnthatthisisexactlywhatIdidtogeneratethescreenshots
shownonpage96. Some Lookand Feelshaveverysubtlechanges,andsomeworkonlyon
specificplatforms.Ifyouaregoingtopickoneorcreateone,pleasemakecertainitworksonall
desktopoperatingsystemsbeforeyoureleaseit.IcannottellyouhowmanyOfficeXPtypelook
andfeelpackagesareoutthere,andeveryoneofthemworksonlyontheWindowsplatform.Gee,
thanks,guys.
IfyouhavenotdonemuchJavaprogramming,pleaseletmetakethetimetopointoutlisting
line78.Wehavenotdeclaredaninstanceofthis,asourclasseshaveallbeensupportorpanel
classesup to this point. An application requires you to construct a frame. The frame is a
containerwithanoptionaltitlethatholdsallothercomponentswhichmakeuptheapplication.
Listingline79isonelineyouwon'tnoticeyouforgotuntilyoutrytoclosetheapplication.If
youstarteditfromthecommandline,yourpromptwon't
return.Thewindowwillbegone,but
theappwillstillberunning.Atthatpointyoueithergetverygoodwithsystemutilitiestofindit,
or reboot and hope your operating system doesn'ttry to help you out by restarting all
applicationswhichwererunningattimeofshutdown.
Aftercallingamethodtocreateourmenubaratlistingline81,Icreateaninstanceofeach
panelandaddeachtothemainPanelalongwithaStringnamesoIcanfinditagain.OnceIhave
allofthepanelsadded,IsetthecontentpaneoftheframetobethemainPanel. Trustme,it
soundsfarmorecomplexthanitis.
WhydoyouthinkIaddedablankpanel?
Comeon,thinkaboutit.WhywouldIaddablankpaneltotheapplicationandgiveitaname
soIcouldfinditagain?Perhapstoclearthescreen?Thatwouldbethecorrectanswer.Iruninto
alotofGUIbasedmenuapplicationswrittenwithalotofdifferenttoolsandalotofthemhave
thesamebug.Onceyouchangethatinitialpanelunderthemenu,theydon't
provideanymethod
ofclearingitotherthanexitingtheprogramandreentering.
ThecreateMenu()methodshowsthefunkyprocessJavamakesadeveloperendurejustto
buildastandardmenu.Toahuman,thewholething,File+Report+dropdowns,isthemenu.In
Swing,FileisitsownmenuaswellasReports.EachmenuishungonthemenuBarandthename
ofthemenuisdisplayedatthatlocationonthemenuBar.

Chapter2MegaZillionaireApplication

164

Please pay attention to the nested ifelse structure starting at listing line 220. Your
assignmentswillrequireyoutocreatenewconditionsinthisstructure.Onceweidentifywhich
menuoptionwaschosenbaseduponthetextofitsactionweneedtoeitherlaunchtheassociated
dialogorshufflethecorrectpaneltothetop.Weneedthenameeachpanelwasaddedwithin
ordertofinditwiththeshow()method.
testMegaXbase.java
1)
2)
3)
4)
5)
6)
7)
8)
9)
10)
11)
12)
13)
14)
15)

import
import
import
import

java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.plaf.*;

import com.logikal.megazillxBaseJ.*;
public class testMegaXbase {
public static void main(String args[]){
MegaXbase mx = new MegaXbase();
}
}

// end testMegaXbase class

We don'thave anything to discuss with this module. I simply needed to include it for
completeness.Mybuildcommandfileisequallynoncomplex.
b.sh
1)
2)
3)
4)
5)
6)
7)
8)
9)
10)
11)
12)
13)
14)
15)
16)
17)
18)
19)
20)
21)
22)
23)
24)
25)
26)
27)
28)
29)
30)

#! /bin/bash
#
#
# rm *.dbf
# rm *.ndx
#
javac -source 1.4 -target 1.4 -d . MegaDBF.java
javac -source 1.4 -target 1.4 -d . StatDBF.java
javac -source 1.4 -target 1.4 -d . StatElms.java
#
jar -cfv megaX.jar com/logikal/megazillxBaseJ/MegaDBF.class
jar -ufv megaX.jar com/logikal/megazillxBaseJ/StatDBF.class
jar -ufv megaX.jar com/logikal/megazillxBaseJ/StatElms.class
#
javac -source 1.4 -target 1.4 -d . MegaXImport.java
javac -source 1.4 -target 1.4 -d . MegaXbaseBrowsePanel.java
javac -source 1.4 -target 1.4 -d . MegaXbaseEntryPanel.java
javac -source 1.4 -target 1.4 -d . MegaXDueElms.java
#
jar -ufv megaX.jar com/logikal/megazillxBaseJ/MegaXbaseBrowsePanel.class
jar -ufv megaX.jar com/logikal/megazillxBaseJ/MegaXImport.class
jar -ufv megaX.jar com/logikal/megazillxBaseJ/MegaXbaseEntryPanel.class
jar -ufv megaX.jar com/logikal/megazillxBaseJ/MegaXDueElms.class
#
javac -source 1.4 -target 1.4 -d . MegaXbaseDuePanel.java
#
jar -ufv megaX.jar com/logikal/megazillxBaseJ/MegaXbaseDuePanel.class
#
javac -source 1.4 -target 1.4 MegaXbase.java
#

Chapter2MegaZillionaireApplication

165

31) javac -source 1.4 -target 1.4 testMegaXbase.java


32)
33) javac -source 1.4 -target 1.4 testNDXBug.java

ThesourcequalifiertellstheJavacompilertorestricttheinputsourceto1.4syntax. We
control the bytecode output by the target switch, which tells the Java compiler to generate
bytecodesequenceswhicharecompatiblewithversion1.4JVMs.
IputalmostallofthiscodeintoaJARfile.Wheneveryouwishtocreateapackageyouneed
toindicatetotheJavacompilerwhereto puttheclassfiles. Thisisdonebythepackage
statementinthesourcefileandthed . switchIputonthecommandline.Thisswitchtellsthe
compilertousethecurrentdirectoryastherootofthepackage.
roland@logikaldesktop:~/mega_xbasej$ ./b.sh
added manifest
adding: com/logikal/megazillxBaseJ/MegaDBF.class(in = 4429) (out= 2404)(deflated 45%)
adding: com/logikal/megazillxBaseJ/StatDBF.class(in = 4152) (out= 2227)(deflated 46%)
adding: com/logikal/megazillxBaseJ/StatElms.class(in = 394) (out= 282)(deflated 28%)
adding: com/logikal/megazillxBaseJ/MegaXbaseBrowsePanel.class(in = 5063) (out= 2713)(deflated 46%)
adding: com/logikal/megazillxBaseJ/MegaXImport.class(in = 5728) (out= 3134)(deflated 45%)
adding: com/logikal/megazillxBaseJ/MegaXbaseEntryPanel.class(in = 20237) (out= 8860)(deflated 56%)
adding: com/logikal/megazillxBaseJ/MegaXDueElms.class(in = 823) (out= 546)(deflated 33%)
adding: com/logikal/megazillxBaseJ/MegaXbaseDuePanel.class(in = 6054) (out= 3189)(deflated 47%)
roland@logikaldesktop:~/mega_xbasej$ dir com
logikal
roland@logikaldesktop:~/mega_xbasej$ dir com/logikal
megazillxBaseJ
roland@logikaldesktop:~/mega_xbasej$ dir com/logikal/megazillxBaseJ
MegaDBF.class
MegaXbaseDuePanel.class
MegaXbaseEntryPanel.class MegaXImport.class
StatElms.class
MegaXbaseBrowsePanel.class MegaXbaseEntryPanel$1.class MegaXDueElms.class
StatDBF.class

Onething whichmaybeconfusingtomanyofyouisthatLinuxuses/ asadirectory


separator,Javauses., andDOS(Windows)uses \ .Ifyoutypedir com.logikal andnothing
appearsit'ssimplyausererror.
2.5 ProgrammingAssignment1
Modify the updateRecord()methodof theEntry module tomake certain no valueshave
changedonthefilerecordbetweenthetimeofreadingandthetimeofattemptedupdate.Ifthey
have,issueanappropriateerrormessageandstoptheupdate. Youcantestthisbychanginga
record,updatingit,thenfindthesamerecordagainandperformanImportbetweenthetimeyou
finditandthetimeyouwriteyournewchangestothedatabase.
2.6 ProgrammingAssignment2
ModifytheEntrymodule bycreatingaclearScreen()method whichconsists ofthecode
foundatlistinglines348through358.Replaceallsuchcodeoccurrencesbyacalltoyournew
method.Testtheapplicationtoensureitisworking.Howmanylinesofcodedidyousave?

166

Chapter2MegaZillionaireApplication

2.7 ProgrammingAssignment3
Modifytheopen_database()methodofStatDBF.javatocheckwhetherthedatabaseisalready
open.Ifitisopen,closethecurrentlyopendatabasebeforeproceedingwiththeopen.
2.8 Progra
mming
As
sign
ment
ogram
mingAs
Assign
signm
ent4
There are currently two reports listed on the menu which do not currently have any
implementationprovided.TheMost OftenHit reportcaneasilybeimplementedbyprovidinga
newclass,MegaXMostElms,whichcomparesonlythehitcounts.YoucanthenclonetheDue
report,changingreportheadings,whileloops,andarraydatatypenames. Becertainyournew
reportrunsfromthemenu!
Youhavetocreatethedumpreportfromscratch.Thereisnothingcomplexaboutit.The
reportwillbeverymuchlikethebrowsewindowexceptthatrecordswillbewrittentoatextarea
insteadofaspreadsheet.
2.9 Summary
ThischapterhasbeenmeanttoprovidearealworldexampletohelpusersnewtoxBaseJ,
andpossiblyevennewtoJava,getuptospeed.Mostoftheexamplesyoufindonlinearevery
oneshot innature.Anattemptwasmadetoprovideyouwithanearlycompletebusinesstype
application.Youshouldbeabletopickandchoosepiecesofthisdesignforyourownuse.Don't
juststealthecode,considerthedesign!
Professionaldeveloperscandesigntheirwayaroundmostofthecriticalweakpointsfoundin
anytoolsettheyareforcedtouse.Peoplewhocutandpastecodewithoutconsideringthedesign
constraintsineffectatthetimetendtoblindlystumbleintoeverycriticalflawknowntoman.
Don'tstumblearound;readtheexplanationwhichhasbeenprovidedfirst.
Younowknowhowtoaddrecordstoadatafile,createanNDXfile,andreadrecordsbothin
indexorderanddirectly. Moreimportantly,youhavebeeninformedofsomeofthebugsand
been showncode which worksaround them. There isno reason youshouldnot be ableto
develop usable applications after having read this entire book. You might think you can
developapplicationsaftermerelyskimmingthischapterandstealingthecode,butyouwouldbe
mistaken. The beginning of this book provides you with questions you need to ask before
designinganyapplication.ThemostimportantquestionofallisS houldyoubeusinganxBASE
filetostorethisdata?

Chapter2MegaZillionaireApplication

167

Too many application developers simply reach for what they used last time without
consideringthelifespanofwhattheywillproduce.Sometimesthislapseleadstooverkill,suchas
acompleteMySQLdatabasetostore50records,andothertimesitisunderkill,suchastryingto
managewhatisnowa100,000itemproductcatalogwithxBASEfiles. Youmustlookatthe
currentneedandthepotentialfutureneedwhendesigninganapplication.
Wheneveryouhaveasystemwhichwillbeusedbyonlyoneperson,willstorefewerthana
couplethousandrecords,andneedstobestandalone,xBASEfilesareagoodoption. Justbe
certainyouaren't
limitingsomeone's
futurewhenyoumakethedecision.Iseealotofcomments
and messages online to the various commercial xBASE engine providers from people and
companieswhohavebeendevelopingasystemwiththetoolinquestionsincethe1980s.Business
keptgrowingandtheykeptcustomizing,andnowtheyissuepleastothevendorstodosomething
aboutthe2GBlimit,astheyhavehadtohacktheirsystemstosupportmultipleprimaryDBFfiles.
LaughallyouwantI'v
e actuallyreadmorethanonemessagelikethatrecently.I'm
not
tryingtodiscourageyoufromusingthistool,I'm
tryingtoeducateyouastoitsproperuse.In
eachcase,thosepoorbastardsstartedoutwithafewhundreditems,butbusinessgrewintoafew
hundredthousanditemsandtheircustomsystemnowcannothandleit.Theyarenowlookingata
completesystemredevelopment,andastheemailssuggest,arewillingtotryanythingtoavoidit.
Certain applications will always lend themselves to a small selfcontained indexed file
system.Ourlotterytrackerisagoodexample.Personalincometaxfilingsystemsareanother.
Smallretailsystemscandoverywell,butyouhavetodevelopcompleteIOclassestocompletely
shieldtheapplicationfromdatastorage.Idomeancompletelyshield.Yourmainapplicationcan
neveruseanobjectfromthelibraryortrapanexceptionfromit. InsteadofhavingyourField
objectspublicasIdid,youhavetomakethemprivateandwriteauniqueget()andset()method
foreachcolumn.Mostofyouwon't
dothis.InsteadyouwilldevelopmuchasIhaveshownyou.
It seems pretty clean at first glance, until you try to replace the underlying database with
PostgreSQLorMySQL.Thenitbecomesarewrite.Ifyouaredesigningforthehereandnow
knowingtherecouldbeamigration,youhavetodesigninthemigrationpathnow,notlater.

Chapter2MegaZillionaireApplication

168

Asacommunityproject,xBaseJisalmostperfect. I'm
notsayingthatitisbugfree, I'm
sayingitistheperfectclassofprojectforacommunity(OpenSource)effort.Thereareliterally
hundredsofplacesonlinecontainingdocumentationaboutthevariousxBASEformats. There
aremanyPython,Perl,andC/C++OpenSourcexBASElibrariesonecanobtainthesourcecode
for aswell.Despitetheir current Javaskilllevel, developersparticipatingintheprojectcan
obtainalltheinformationtheyneedwithouthavingtohavealargesupportforum.Youcaneven
testyourchangesforinteroperabilitywiththeotherOpenSourcexBASEprojectssoyoudon't
have to wonder if you did them correctly. If it works cleanly with two other OpenSource
products,youdiditcorrectlyenoughfortheOpenSourcecommunity. Remember,thereisno
ANSIstandardforxBASE.WhatreallymattersisthatallofthestuffintheOpenSourceworld
workstogether.Don't
forgetthatOpenOfficeandKSpreadbothprovidetheabilitytoopenaDBF
filedirectly.Besuretotestyourresultswiththeseapplicationsaswell.SomedayIBMmayeven
adddirectsupportforDBFfilestoLotusSymphony.
2.10
2.10ReviewQuestions
1.
2.
3.
4.
5.

WhatdoesCUAstandfor?
WhatisthedefaultlookandfeelforJavaapplications?
WhatDBFmethodtellsyouifarecordhasbeendeleted?
UnderwhatconditionsisitokaytoloadDBFcontentsintoaspreadsheet?
AfteropeninganexistingDBFandattachingoneormoreexistingNDXfiles,whatshould
youdo?
6. Are the various Field variable names required to match their corresponding xBASE
columnnames?
7. WhatinterfacemustaclassimplementinorderforittobeusedwithanArrays.sort()call?
8. Doesthestatement:
MegaXDueElms d_elms[] = new MegaXDueElms[ELM_COUNT];

completely allocate an array of MegaXDueElms or do you still have to create each


individualelement?Ifcreate,whatisthesyntaxtocreateanelement?
9. WhatStringmethodmustbeusedwhenattemptingtoconverttheresultofaget()fora
NumFieldtoanInteger()?Why?
10. Whatjavaccommandlineoptionrestrictsthecontentofyoursourcecode?

Chapter3
Chapter3
Ruminations
Someofyoumaynotbefamiliarwiththisbookseries,soletmeexplain.Itrytoendeach
onewithachapternamedRuminations.TheseareessaysaboutITandlifeingeneralmeantto
makeyouthink.Somemayverywellpissyouofformorallyoffendyou.Sobeit.Somemay
causeyoutoshoutoutsupportonacrowdedtrainorplanewhenmosteverybodyisasleep.Sobe
it.
Inshort,thischapterismyrewardforwritingthebookandmayverywellbeyourrewardfor
readingit.Onthewhole,youshouldtakeawaysomethingfromeachessaywhichmakesyoua
moreworldlyITdeveloper,ifnotabetterperson.
Enjoy!
3.1 AuthoritativeResources
TheeventsI'm
abouttorelateactuallyoccurredwhileIwaswritingthisbook.Theprimary
developer/creatorofthexBaseJOpenSourceprojectwasshocked,toputitmildly,thatIdidn't
use
EclipseforJavadevelopment. HementionedsomeAuthoritative Resources claimingsome
extremelyhighpercentageofdevelopersusedEclipsetowriteJava.I've
beeninITlongenough
toknowthetruthaboutthoseauthoritative resources, sopleaseallowmetoshedsomelighton
thesubject.
Mostofthese AuthoritativeResources comefromapublisherorasupposedlyindependent
analyst.Thevastmajorityalsopointtoasurveydonebysomestandardsbodytohelpreenforce
theirownmanufactureddata.Suchsurveysdrawinthegullible(readthatMBAs)whohaven't
goteventheslightesttechnicalclue.Inshort,peoplerunningaroundquotingtheseauthoritative
resources withoutanyconceptofhowthedatawasobtainedarenaivelyhelpingtoperpetratea
fraud.
Itisnotofteninthisbookseriesthatyouwillfinditspoutinganypercentagepulledfromany
placeotherthanmydirectexperiencesinlife.Iholdthisseriestoamuchhigherstandardthan
the mass market publishers hawking everything from romance novels, to cook books, to
supposedlyscholarlytomesonIT.Thecontentinthisbookseriescomesfromatrenchwarrior,
notawishfulthinkeroramarketingdepartment.

170

Chapter3Ruminations

GiveneverythingIhavejusttoldyou,itshouldcomeasnosurpriseIwroteanessayonthis
whenthedeveloperquotedamassmarketpublisherandastandardsbodyasthesourceof his
information.TheversionpresentedhereisaslightlymoresanitizedversionofwhatItoldhim.
Yousee,heforgotthathewastalkingtonotonlyanauthor,butabookpublisher.Iknowthe
publishinggame.AtleastIknowthekindofgameplayedbythemassmarkethouses.
WhenmassmarketPublisherXputsoutabunchoffree informationstatingthatN%ofall
developersarecurrentlyusingtechnologyZyoucanprettymuchbetthatthisinformationwas
writtenbythemarketingdepartmentandhasabsolutelynobasisinfact. Youcanprettymuch
provethisbylookingattheirtitleselectionandcountingthenumberoftitlestheyhaverecently
releasedcoveringcompetingtechnologies.YouwillfindfifteentotwentytitlescoveringtheN%
technologyanditscomplimentarytopics(anexamplewouldbeEclipse+Java+OOP)andatbest
tworecenttitlesoncompetingtechnologies(Kate,SciTE,jEdit,TEA,C/C++,Python,etc.)
Ther ecent qualifierismostimportant.YouwillfinddozensoftitlesonC/C++fromthe
massmarkethouses,butveryfewpublishedinthepasttwoyears.Themassmarkethouses(and
Microsoftforthatmatter)makemoneyonlyiftechnologyiscontinuallybeingreplaced.Youwill
findbothmassmarketpublishersandretailsoftwarevendorstryingtocreatetrendswherenone
exist(orareneeded)simplytogeneraterevenue.Thevastmajorityofpeopleworkingforeither
vendorwillcompletelyignorethefactthatonceabusinessapplicationiswrittenandinplace,it
tendstostayaroundfor30years.
Oh,please,don't
takemywordforhowlongbusinessapplicationsstayinplaceoncewritten.
SimplysearchthroughthenewsarchivesforallofthoseY2Kstoriestalkingaboutsystemswhich
havebeenmakingmoneyforcompaniesforroughly30years. Whenyouaredonewiththat,
searchforallofthecurrentstoriesaboutHeritage DataSilos andLegacy Systems. Any
applicationmorethaneightyearsoldtendstofallintotheheritage category.WhenMicrosoft
founditselfbeingviewedasalegacysystemwithXP,theyquicklytriedtoreinventthemselves
asanIndiansoftwarecompanyviathereleaseofWindowsVista.Itwassucharousingsuccess,
with highranking business officials from around the globe giving interviews stating their
companies would never upgrade that Microsoft had to pull a lot of the development back
onshore and put out abillable bugfix labeled Windows 7. They have removed theword
Vista frommosteverythingtheycontrol.Onceagain,gosearchforthearticles,don't
justquote
me.

Chapter3Ruminations

171

So,whenmassmarketPublisherXtellsyouthat80%ofalldevelopersareusingtoolZ,you
lookandseethattheysaidthesamethingeightyearsagoabouttoolCandhaven't
publisheda
bookontoolCinthepast6years.Ohmy!TheycurrentlyhaveelevenbooksinprintontoolZ
andandasearchoftheBooksInPrintdatabaseshowstheyhavefourmorebeingpublishedthis
year.WhathappenedtoallofthoseapplicationswrittenusingtoolC?Hmmm.Naryawordis
spokenaboutthat.Businessesmustrewrite100%oftheirapplicationseachandeverytimemass
marketPublisherXwantstosellmorebooks.Nowonderbusinessesarehavingtroublemaking
money!
Of course Publisher X will try to quote some survey from a recognized standards or
intellectualbodylikeIEEEorACM.IEEEisastandardsbody.I've
neverbelongedtoIEEE,but
IhavebelongedtoACMandDECus. IcurrentlybelongtoEncompass. Letmetellyouhow
numbersgetgeneratedbysuchorganizations.Theysendoutasurveytotheirmembers.Those
whorespondrightawayarethoseworkinginacademiaandaredesperatetohaveaquoteprinted
somewherebecausetheyareinapublishorperishworld. Everyoneelse,thoseworkingfora
living,filetheemailawaysayingtheywillgettoitwhentimeallows. Lessthan1%ofthose
peopleactuallygetaroundtoresponding,whichisroughlythesamenumberofmemberswho
bothertovotefortheorganization's
leaders.So,whenanorganizationlikethisputsoutanumber
saying80%ofitssurveyedmembersusetoolZ,it's
really80%of1%.Thenumberisvalidasfar
asitgoes,butitdoesn'tgoveryfar.
Ifyouhaveneverbeenavotingmemberofanyorganization,youprobablydon't
haveagood
frameofreferenceforthis. Yousee,mostoftheseorganizationsprovidesomehooktokeep
members,thenprettymuchdisregardthewishesoftheirmembership.Inonecase,youhavetobe
amembertoobtainafreeHobbyistoperatingsystemlicense.Theorganizationthenturnsaround
andcompletelydisregardsanyandallwhohaveaninterestinthatoperatingsystembecausethe
tokenfewontheboardarebusytryingtokissuptoavendorchampioningafarlesscapable
operating system. Nomatterhowmanypeoplecastballots,onlythenamesintheleadership
change,nottheactualleadership.Mostgettiredoffightingthebattle.Theytakethefreelicense,
dowhattheyneedtodowithit,andletthescamcontinue.
Thescamgetsevenworsewhenmarketingfirmstrytorelabelaportionofthemselvesas
industry analysts. IcannottellyouhowmanyMBAsgethandedfourcolormarketingglossies
andhonestlybuythestorythatthiswasindependentindustryresearch.
Simplyput,almostnobodygoestoprisonforwireormailfraudthesedays,certainlynotthe
marketing departments for publicly traded companies. This means that there are no
Authoritative Resources forcurrenttrendsinsoftware,simplymarketingfraud,whichmostare
conditionedtofollow.

172

Chapter3Ruminations

3.2 TimestampsonReports
Thiswon't
bealonglecture,butitisoneIneedtocoverinthisedition.Hopefully,youhave
beenaloyalreaderfromthelogicbookthroughthefirsteditionofthisapplicationdevelopment
book,Java,SOA,andnowthisverybook. Thisdiscussionwillmakemoresensetomyloyal
readersthanmycasualreaders.
Duringthe70sandearly80swhenjobswererunonlydailyorweeklywewouldputthedate
ontheflagpageandsometimesonthefirstheadingline.Somereportwritingstandardshadeach
jobcreatingitsownheading page or coverpage asthefirstpageinthereportfile.Thispage
wouldbetheonlyplacenondetailleveldateinformationwasplaced.
Beforetheyoungwhippersnappersreadingthisgetallupinarmsaboutourstandards,letme
paintapictureofthetimes.Thevastmajorityofreportscamefrombatchjobsrunbycomputer
operators. Thesejobswereeitherpartofaregularlyscheduledproductioncycle,ortheywere
requestedbyvariousmanagerso ndemand andtheleadoperatorwouldworkthemintothe
schedule. (Notalldatawaskeptonlinebackthen,sojobshadtobescheduledbasedupon
availabilityoftapeandremovablediskdrives.)Therewasnosuchthingasapersonal printer
andveryfew workgroup printersscatteredaroundthecampus fromwhich userscouldprint
theirownreports. (Intruth,probablythebiggestdriving forcebehindfloppybased personal
computersgettingintocompaniesprobablywasn't
thesoftware,butthefactyoucouldget$300
dotmatrixprinterstogowiththem.Anofficeworkerwhogeneratedalotofpaperhadabetter
chance of surviving a layoff than an office worker who simply fetched coffee and attended
meetings.)
Themostimportantthingforyoutorememberisthatprinterswereexpensive,noisy,and
used tractorfed continuous form. Unlike today'slaser printers which willtake one or more
bundlesofcopierpaperandquietlyhumouthundredsofduplexprintedpagessittingonatablein
thecenterofaworkgroup,earlyprintershadtobekeptinthemachineroombecauseofthesound
dampeningandsecurityprovidedbythatroom.Mostreportsofthedaywerefinancialinnature.
You certainlydidn'twantjustanybody knowing youwere120dayspastdueforallofyour
vendors.
Manyjobswouldcomeoffthesystemprinterbeforeanoperatorgotaroundtoseparating
them.Normallythebatchjobscreatingthemwouldprintthereportwithastatementlikethis:

$ PRINT/FLAG/BURST/NOTE=Deliver to Roland

created ''f$time()'

some.rpt

Chapter3Ruminations

173

The/BURSTqualifierwouldcausetwoflagpageswithaburst bar printedbetweenthem.


Aburstbarwassimplyabunchofcharactersprintednearthecommonedgeoftheflagpages
severallinesdeep.Thismadeiteasyforoperationsstafftofindthereportswhenflippingthrough
thepileofpaperinfrontofthem.The/NOTEcontentswouldbeprintedinaspecificlocationon
eachflagpage.Fulltimeoperationsstaffknewtolookforthis.Asturnoverincreased,andgood
letterqualityprinterscameintobeing,itwascommon foroperationsstafftohaveaprintjob
whichprintedupnicecoversheetsforallregularlyscheduledjobs. Theoperatorwouldthen
staplethecoversheet(whichsaidwhoreceivedthisparticularcopy)tothefrontofeachreport
copy.Theoneoffjobsstillrequiredmoresenioroperationsstafftoensureproperdelivery.
Weweren't
obsessedwiththetimeportiondisplayedontheflagpageofareport,unlesswe
hadsomeservicelevelagreementforsomemucketymuckthatabsolutelyhadtohaveReportA
inhishandsby9:05A.M.Yes,I've
workedforpeoplelikethatbefore.Ievenstuckaroundone
dayafterthereportwasdeliveredlatejusttoseewhathedidwithit.Hehandedittohissecretary
whoputitinabinderandfileditinacabinet.Westillgotbeatupfordeliveringlate,butword
gotoutthathedidn'tactuallyusethatreport.
During the mid 1980s we started to see cheap serial printers scattered about company
locationsandprintqueuescreatedspecificallyforendusers.Someofourbatchjobsevenstarted
toprintreportsdirectlytothoseprinterssooperationsdidn't
havetodeliverthem. Onceusers
startedbeingabletorunreportsfromtheirmenuswehadtostarthavingreportingstandards
whichplacedthetimeontheveryfirstlineofeverypageheading.Mostshopsreportedthetime
ontheleftmarginofthefirstlineasI'v
eshownyouinthisbook.Thetimeremainedthesamefor
an entire report. We didn'tgo out and snag a new timestamp each time we printed report
headings.Mostuserswereokaywiththetimebeingconsistentonallpageheadings.
Westartedgettingintotroublewithlongrunningjobsthatusedsharedindexedfiles.Wegot
intoevenmoretroublewhenrelationaldatabaseswereintroducedintothemixalongwithlower
diskprices. Reportswhichtookhourstoruncouldhavesometransactionscomeintothefiles
whichwouldskewresults. Tohelpyouunderstandthepain,trythinkingaboutwhathappens
whenabillingjobforacreditcardcompanyisrunningwhiletransactionsareposting.
BillandFredbothwalkintothesamestorewiththeircreditcardissuedbythesamecompany
andhavethesame27daybillingcycleendingontheverysameday.Theycheckoutattheexact
sametimeontwodifferentregisters.Bill'sac
countbeginswith04andFred's
accountbeginswith
98. Bill's
statementhasalreadybeencreatedatthetimetheauthorizationwentthroughsohis
chargewon'tappearuntilnextmonth.
ItjustplainsuckstobeFredatthispoint.

174

Chapter3Ruminations

Thisistheeraofthe24hourlifestyleandinstantgratification.Largebatchjobslikecredit
cardinvoicingstillhappen. Companies willcreatedozens,ifnotthousands,ofprintfileseach
containing hundreds, ifnot thousands, of individual credit card statements. Insomeobscure
location,andpossiblyencrypted,eachstatementwillhavethetimestampprintedonit. Itmay
evenbeprintedusingaverysmallfontinwhatlookslikearunningpagefooter.Employeesof
thecreditcardcompanywillknowhowtoreadit. Theywill divulge thisknowledgeduringa
customerservicecallwhenexplainingtoahusbandandwifewhomadechargesattheexactsame
timeintheexactsamestoreontheirpersonalcreditcardswhyonlyonehadthechargeshowup
onthestatement.
WhilemanyreportsontheInternetwillbegeneratedbysomeJavaorJavascriptcallsto
backendreportservices,theservicesthemselveswillmostlikelybeonatrustyOpenVMScluster
oranIBMmainframe.TheWebpageswhichwillreceivetheinformationwillbeinthebusiness
ofaggregatingthisinformationfromthedifferentreportingservicestheyarecalling.Itisreally
funnywhentheydon't
bothercoordinatingas of timestampsfromthevariousbackendservices
priortodisplay.
Onceagainwecanusecreditcardsasanexample.Manypeoplehaveacreditcardwhich
willgivethemsomekind ofaward,suchasairlinemilesorhotelbucksorsomesuchthing.
When a user opens up an online accountto monitor this information he or she usually gets
presented some kind of su mmary screen. The summary screen will coordinate responses
receivedfromallofthebackendservicesintoonecohesivedisplayshowingcurrentcharges,
payments,mileage,hotelbucks,etc.Mostoftheresponsesitchewsontocreatethedisplaywill
bestoredinsometemporarystorageontheWebbrowsingcomputer,usuallyintheformofa
cookie,butcouldbeanything.Thingsgethilariouswhenoneormoreofthebackendservices
aredownbutyouhaven't
deletedallyourcookiesnoremptiedyourcachesinceyourlastvisitto
thesite. Youlookatasummaryreportshowingyourlasttime's
awardedmileage(orcharges)
withthistime's
charges(ormileage).Thesethingsgettobeaproblemwhenyouarelookingto
useupyourmilesforyourvacation.
ManyPCbasedoperatingsystemswillonlygiveyouatimestampdowntothenumberof
secondssincemidnightJanuary1,1970.Thatlevelofresolutionisnotenoughfortoday's
world.
Itmightgetyoubyonareportheading,butnotatthetransactionlevel.Giventhatmostofyour
timeargumentsaregoingtobeabouttransactionleveldata,youneedtoprovidesomemethodof
defending yourself when the argument becomes why did this transaction show up on this
invoice? Ifyourreportpageheadingcontainsonlythestarttimeandthereisnoreportfooter
containingthecompletiontime,howdoyoudefendyourself?

Chapter3Ruminations

175

Letmeleaveyouponderingreportingtimestampswiththislittleditty.
Atanygivenstockexchangearoundtheworld,therewillbeorderscominginatafurious
pacealongwithconsolidatedlastsales.Orderswhicharen't
ma rket orders(meaningtobuyor
sellatthema rket price)getplacedinthebooktobecomepartofthecurrentmarket.Theactual
currentmarketpriceisdeterminedbytheconsolidatedlastsale,whichisgeneratedeverysecond
orso(longertimespansforlightlytradedstocks)bysaleswhichare printedtotape. The
consolidatedlastsalehastheeffectofcausingordersinthespecialist's
booktosuddenlybeduea
fill,andinsomecasestheseautomaticallyexecute.Anyexecutedorderhasitssaleinformation
senttotheprimaryexchangeforthatissuetobecomepartofthenextconsolidatedlastsale.Itis
calledconsolidated becauseitisacalculatedvaluebaseduponallsalesprinted totape during
thecalculationtimespan.(Tradeinformationusedtoprintonpapertapescalledticker tapes.
Nowthattradeinformationissenttotheprimaryexchangeinaparticulardataformat,butthe
lingoprintedtotapestillexists.)
Thingsgetdiceywithmarketorders.Manyexchangescanallowamarketordertobebriefly
heldformanualpriceimprovement.Thereisnofearofthemarketmovingbecausethemarket
orderisrequiredtobetaggedwiththeconsolidatedlastsalevaluethatwasineffectatthetimethe
marketordercameinorcurrentlastsaleprice .
Thislastparagraphsoundskindofsimple,doesn't
it?Itis...untilyougetaroundtodefining
current . Highvolume stocks with millions or billions of transactions per day can require
current tobedowntothenanosecond.Nowwegetintotheissueofin effect. Technically
thatconsolidatedlastsaleisin effect thenanosecondtheprimarymarketsendsitout. The
realityisthatittakesmorethanananosecondforthatlastsaletotransmitfromonecoasttothe
otherandberecorded.Onceitisrecorded,allofthebook checking thenhappenstoseeifany
fixedpriceordersarenowdueafill.
WhenIwasjuststartinginJuniorCollege,thistimethingwassomethingonlyacademics
thoughtabout.Wehaddiskseektimesmeasuredinseconds,andthoseseeksonlyhappenedonce
anoperatorgotaroundtomountingtheremovabledisk...ifwewereluckyenoughtobeusinga
disk.NowthatI'm
approachingtheautumnofmycareeritisbecomingarealissue.Tothoseof
youjuststartingout,whatcanIsayotherthan,itsuckstobeyou,Fred.

176

Chapter3Ruminations

3.3 DoomedtoFailureandTooStupidtoKnow
I get a lot of phone calls and emails for contracts. There are few phone calls more
entertainingthanlarge projecttogetoffofOpenVMS. Inevitably,theseprojectsarerunbythe
BigNconsultingfirmswithalloftheactualworkwhoredouttopreferred vendors. Recently,I
gotanotheroneofthesecalls.Hereisprettymuchhowitwent.(Callerisinblue.)
HowmanyyearshaveyouworkedonOpenVMS?
20plus.
HowmanyyearshaveyouworkedwithathingcalledPowerHouse?
It's
notathing,itisa4GLanditwasagodsendwhenitcameout.I've
beenworkingwithit
sincetheVAX11/750wastheprideofmostshops.
Howlongisthat?
Well,DECwasstillinbusiness...1985?ShortlyafterthatDECcameoutwiththeAlpha.
Ohwow.WehaveaprojecttomigratefromaVAX.Ididn't
realizethehardwarewasthat
old.
Most likely you have a project to migrate from either an Alpha or an Itanium. Since
OpenVMSmigratedfromVAXtoAlphatoItanium,mostpeoplestillcallthehardwarewhich
runsitaVAX.Thisiswrong,anduntilweendteachertenureinthiscountry,theproblemisn't
likelytoimprove.
Oh.WellI've
beenmoreinvolvedintherequirementsgatheringphaseoftheprojectforthe
pastsixmonths.Weareonlynowstartingtolookattheexistinghardware.Theyhavesomething
calledamultinodeclusterrunningRDB,haveyoueverworkedonthathardware?
Once again, it wasOpenVMS. Multiplemachines could beclusteredtogether toreally
increasecomputingpowerandfaulttolerance.I've
workedonclustersthatrangedinsizefrom
two machines in a room to lots of nodes scattered around the globe. From a development
standpointitreallydoesn'tmakemuchdifferencehowmanyorwheretheyare.
Well, I'msure that will come up. We are looking at replacing it with an OpenSource
platform.

Chapter3Ruminations

177

Well, if they were using RDB on a multinode cluster, odds are they needed the fault
tolerancethecombinationprovided.YoucannotcreateafaulttolerantsolutionviaUnix/Linux
becausetheOSkerneldoesn't
havetheconceptofarecord.Withouttheconceptofarecord,you
cannot have a kernel level lock manager. Withouta kernel level lock manager, you cannot
developalocaltransactionmanager. Withoutalocaltransactionmanageryoucannotbuilda
distributedtransactionmanager. Withoutbeingabletodistributetransactionsacrossacluster
withtwophasecommit,youcannotcreatefaulttolerance.
I'msurewewilldiscussfaulttoleranceatsomepoint.
You mean to tell me you've burned through 6 months of billable hours gathering
requirements and never discussed fault tolerance? You never asked if their current system
providedguaranteeddelivery+guaranteedexecution?
Wewillbeusingamessagequeuethatprovidesguaranteeddelivery.
Withouttheguaranteedexecutionpart,deliverydoesn't
matter. Adistributedtransaction
managerlikeDECdtmallowsyoutohaveasingletransactionwhichencompassesamessagefrom
an MQ Series message queue, multiple RMS Journaled file, RDB, ACMS, and every other
productwhichwasdevelopedtoparticipateinaDECdtmtransaction.Ifthattransactionisspread
acrossnine nodesin acluster andoneofthosenodes goesdownmidprocess, DECdtmwill
rollbackthetransaction.IfthetransactionwasalsopartofanACMSqueuedtask,ACMSwillre
dispatchthetransactionuptoNtimesuntiliteithersuccessfullyexecutesorexceedstheretry
countandhastoberoutedofftoanerrorhandlingqueue.
Oh.Well,thosediscussionswillhappeninacoupleofmonths.Icantellyouhavealotof
experienceonthisplatform,soI'm
goingtopresentyoutotheprimaryandoneofthemwillget
backtoyouforatechnicalinterview.
YoumeantotellmetheyactuallyhaveOpenVMSprofessionals?
Well,itwillprobablybesomeonefromHRexplainingthenoncompeteandotherpolicies.
I originally wrote the above essay when I was working on the second edition of The
MinimumYouNeedtoKnowtoBeanOpenVMSApplicationDeveloper. Thateditionwon't
be
outforacoupleofyearsandmostofthepeoplewhobuy/readthatbookliveouttheabovescene
far toooften. Itis more appropriatefor metoplace sucha story here, wheremany of the
problemswillcomefrom.

178

Chapter3Ruminations

Oh, don'tgo getting alldefensive now. If that statementoffended you itis mostlikely
becauseyoudon't
knowenoughabouttheindustrytounderstandthetruthofthematter.Wedid
notcoverwritingfaulttolerantapplicationsinthisbookbecause doingso isnearlyimpossible
usinganxBASEfileformatandhavingnocontrollingengineprovidingabarrierbetweenall
applicationsandthedata.Evenifyouusedthislibrarytocreatesuchanengine,youwouldhave
toensurenofileusedaneasilyrecognizableextensionandthattheenginehaditsownuserID
whichownedalldatafilesanddidnotallowsharedaccess. Evenifyouachievedallofthese
things,youwouldnothaveprovidedfaulttolerance.Thesethingsfixonlythedatasharingand
indexproblemswhicharenotoriouswithxBASEdatafiles.
Faulttoleranceisasdescribedabove,thetransactionscontinuenomatterwhatfails.
Thinkaboutthatline.It's
notjustanacademicmantra.Itisabusinessphilosophywherea
BRP(BusinessRecoveryPlan)documentisn't
createdbecausethesystemsaredesignedtonever
allowthebusinesstofail. Youwillfindthevastmajorityofpubliclytradedcompanieseither
haveacompletelyuntestedBRPinplace,orhavesimplynevergottenaroundtowritingone.A
tokenfewcompaniesdesignapplicationsandinfrastructuretosurviveanything.
When thetwin towersfellon 9/11 there werecompanies in those buildings using every
operatingsystemknowntoman.ThecompanieswhichwereusingdistributedOpenVMSclusters
hesitatedforupto15minuteswhiletheclustermadecertainallhardwareatthelocationhad
ceasedtorespond,thencontinuedprocessingeachandeverytransaction.Notransactionwaslost.
Despite the loss of life and location, the company neither ceased operation nor went out of
business.Everycompanybasingitsbusinessonotherplatformsstoppeddoingbusinessthatday.
Manyneverreturnedtobusiness.
I'm
tellingthisstoryinabookcoveringJavaandxBASEdevelopmenttoprovideyouwith
someconceptofthelimitationsyourtoolsimpose. Developerstendtobecomeenamoredwith
theirchoiceofdevelopmenttools.Theyrunaroundtryingtousetheminsituationsinwhichthey
arecompletelyinappropriate.IhavesatinconferenceroomswhereClipperhackersproposedto
useasystemwritteninClippertomeetalltheaccountingneedsofaFortune500company.They
wanted all data stored on a single instance of a file server which had some minimal RAID
capabilitiesandhonestlybelievedthatwasgoodenough.Don'tyoumakethissamemistake.

Chapter3Ruminations

179

EarlyoninthisbookIwalkedyouthroughthethoughtprocesswhichhadmeselecting
xBaseJ as a development tool for a project. Granted, the project I ended up writing wasn't
providedinthisbook,butitisoutthere.YoucanfinditonSourceForge:http://sourceforge.net/
projects/fuelsurcharge/ IusedthelotteryapplicationbecauseIalwaysusethatapplicationin
ordertolearnorteachnewtools.Thethoughtprocessusedtoselectthetoolsistheimportant
part,nottheapplicationIendedupwriting.
RereadtheconversationIhadwiththepersonwhocalledaboutthecontract. Theyhad
burnedsixmonthsandnotcoveredthemostimportantquestion. Theyhadnotaskedthefirst
questionwhichmustbeaskedatthestartofeachrequirementsgatheringsession.What isthe
expectedavailabilityofthissystem?
Woulditsurpriseyoutolearnthattheprojectwasforahealthcarecompanyandthatthe
systembeing replaced washandling patientrecords along withprescriptionsand pharmacy
dispensing? Wouldyoubeshockedtolearnthatmanyofthepatientsbeinghandled bythis
systemweresufferingfromHIVandcouldenteranemergencyroomonanygivenminuteofany
given24hourperiodandthatitmightnotbeahospitalwheretheirdoctorworksorevenbein
theirhomecityorcountry?
IfyouthinkyoucanuseJavaonaplatformwhichdoesn't
provideadistributedlockmanager
integratedintotheoperatingsystemkerneltodeliverasystemforthatenvironment,youaren't
anybetterthanthepersonwhocalledmeabouttheproject. Inmostcases,thecombinationof
high availabilityandfault tolerancepreclude theuseof anykind ofVM. Ingeneral, aVM
designedtorunonmultipleplatformscannotmakeuseofadistributedlockmanagerwhichwas
integratedintotheOSkernelofoneplatformbecausethelesserplatformstheVMrunsondon't
haveaprayerofeverhavingsuchatool.Ifyoustore100%ofalldatainarelationaldatabase
whichisnativetotheplatformprovidingthedistributedlockmanagerandintegratedwithsaid
manager,andyouhaveamessagequeueingsystemwhichisintegratedwiththedistributedlock
manager,andamessagedispatchingsystemwhichisnotonlyintegratedwiththedistributedlock
manager,butwillrollbackandredispatchthemessagewhentheprocesshandlingithangsor
dies,thenandonlythen,canyouthinkaboutusingaVMbasedlanguagefordevelopment.Yes,
therewerealotofandsinthatsentence,andforgoodreason.
Beforeyoucangooutworkingintherealworld,youneedtoknowtwothings:
1. Thelimitsofyourtools.
2. ThefirstquestionyouaskisWhatisthissystem'sexpectedavailability?

180

Chapter3Ruminations

Availabilitywasn't
anissueforthelotterytracking application. Itwasmeanttobefora
singlepersonandthedatabasecouldbecompletelyrecreatedfromaCSVfileinjustafewsteps.
AllofthedatawhichwentintotheCSVfilewouldbegleanedfromsomestatelotterysystem
Webpage,soeventheCSVcouldberecreatedfromscratchifneeded.Thistypeoflotteryhas
drawingswhichhappen,atmost,afewtimesperweek,soarecoveryprocesswhichtakesadayis
fine.
Let's
contrastthoserequirementswithanapplicationwhichmustbeabletoprovidemedical
recordstoanyhospitalintheworldforanypatientcontainedinthesystem.Doyouthinka12
dayrecoveryperiodisacceptable?Justhowlongdoyouthinktheemergencyroomhaswhena
patientcomesinalreadyseizingandthestaffneedstoidentifywhichmedicationthepatienthas
beenonbeforegivinghimsomethingtohelpwiththeseizing?Howdoyouaccomplishsystem
and data backup yet provide the necessary availability? How do you do OS and hardware
upgradeswithoutimpactingavailability?
As the availability requirement goes up, your tool choices go down and the number of
mandatoryquestionsrises. Witha12dayrecoveryperiod,wedidn't
askabouthardwareand
operatingsystemupgradesbecausetheydidn't
matter.Whenasystemisneeded24x7x365these
questionsbecomeimportant.Nevermakethemistakeofassumingyoucandoanythingyouwant
withlanguage A or toolZ. Theavailabilityrequirementsdictatethetools.Ifmanagement
cannotbemadetounderstandthis,youhavetoeithereducatethemorleavewithoutwarning.

Appe
ndi
xA
ppendi
ndix
AnswerstoIntroductionReviewQuestions:

HowmanyfieldsdiddBASEIIIallowtobeinarecord?
128
WhatgeneralcomputingtermdefinesthetypeoffileanxBASEDBFreallyis?
Relativefile
WhatdoesxBASEmeantoday?
Itreferstothedatastorageformatusedbyvariousapplications.
WhatwasthenoncommercialpredecessortoallxBASEproducts?
Vulcan
IntermsofthePCandDOS,wheredidthe64Kobject/variablesizelimitreallycomefrom?
TheLIM(LotusIntelMicrosoft)EMS(ExpandedMemoryStandard)
WhatcompanysoldthefirstcommercialxBASEproduct?
AshtonTate
IsthereanANSIxBASEstandard?Why?
No
EachofthevendorswanteditsownproducttobethestandardputforthbyANSIandthey
refusedtoreachanycompromise.
WhatisthemaximumfilesizeforaDBFfile?Why?
2GB.Thatisthemaximumvaluefora32bitsignedinteger.
WhatwasthemaximumnumberofbytesdBASEIIIallowedinarecord?dBASEII?
4000bytes;1000bytes.
Whatform/typeofdatawasstoredintheoriginalxBASEDBFfile?
Character.Numericfieldswereconvertedtocharacterrepresentationbeforestoring.
CanyoustorevariablelengthrecordsinaDBFfile?
No.

DoesanxBASElibraryautomaticallyupdateallNDXfiles?
No.Itisonlyrequiredtoupdatethosewhicharebothopenedandattached.
WhatistheacceptedmaximumprecisionforaNumericfield?
15.9:Totalwidthof15with9digitsofprecision.
Whatisthemaximumlengthofafieldorcolumnname?
10characters

AnswerstoChapter1ReviewQuestions

Whattwosituationsforceauserorapplicationtophysicallyremovedeletedrecords?
1)TheDBFreachesthemaximumfilesize.
2)Thediskholdingthedatafilerunsoutoffreespace.
Bydefault,whatarestringandcharacterfieldspaddedwithwhenusingxBaseJ?
Nullbytes.
IfyouhaveaDBFopenwithNDXfilesattachedtoitthencallasubroutinewhichcreatesnew
NDXobjectsforthosesamefilesandcallsreIndex()onthem,willthechangestotheindexfilesbe
reflectedintheNDXobjectsyourDBFholds?Whyorwhynot?
No.
NDXobjectsloadtheentireBtreeintoRAManddonotmonitordatafilechanges.
WhattwoJavaclassesdoyouneedtousetobuildcreateareportlinemakingthedatalineupin
columns?
StringBuilderandFormatter.
HowdoesonetellxBaseJtopadstringandcharacterfieldswithspaces?
Util.setxBaseJProperty("fieldFilledWithSpaces","true");
WhatDBFclassmethodphysicallyremovesrecordsfromthedatabase?
pack()
WhatisthemaximumsizeofaDBFfile?
2GB

WhatDBFclassmethodisusedtoretrieveavaluefromadatabaseFieldregardlessoffieldtype?
get()
AftercreatingashinynewDBFobjectandcorrespondingdatafile,whatmethoddoyouuseto
actuallycreatecolumnsinthedatabase?
addField()
WhatDBFclassmethodisusedtoassignavaluetoadatabaseField?
put()
WhatDBFclassmethoddoyoucalltochangetheNDXkeyofrefernece?
useIndex()
WhatDBFclassmethodignoresallindexesandphysicallyreadsaspecificrecord?
gotoRecord()
Whenyoudeleteadatabaserecord,isitactuallydeleted?
No,itisflaggedasdeleted.
WhatDBFclassmethodsetsthecurrentrecordtozeroandresetsthecurrentindexpointertothe
rootofthecurrentindex?
startTop()
WhatisthemaindifferencebetweenreadNext()andfindNext)?
readNext()requiresakeyofreferencetohavebeenestablishedviasomeotherI/Ooperation
andfindNext()doesnot.
Whatfunctionormethodreturnsthenumberofrecordsonfile?
getRecordCount()
Whathappenswhenyouattempttostoreanumericvaluetoolargeforthecolumn?
Atruncatedversionofthevalueisstored
Whathappenswhenyouattempttostoreacharactervaluetoolargeforthecolumn?
Anexceptionisthrown
Whenaccessingviaanindex,howdoyouobtaintherecordoccurringbeforethecurrentrecord?
findPrev()orreadPrev()

WhatDBFmethodreturnsthenumberoffieldscurrentlyinthetable?
getFieldCount()
Whenretrievingdatafromadatabasecolumn,whatdatatypeisreturned?
String
WhatisthemaximumlengthofacolumnnameformostearlyxBASEformats?
10
Whatdoestheinstanceofoperatorreallytellyou?
Whetheranobjectcanbesafelycastfromonetypetoanother.
AredescendingkeysdirectlysupportedbyxBaseJ?
No.
WhatNDXmethodcanyoucalltorefreshindexvaluesstoredintheNDXfile?
reIndex()
WhatJava StringmethodallowsyoutosplitaStringintoanarrayofStringsbasedupona
delimitingString?
split()
DoNDXobjectsmonitordatabasechangesmadebyotherprogramsorusers?
No
Canyou"undelete"arecordinaDBFfile?Ifso,whyandforhowlong?
Yes.
Records are only flagged as deleted; they are not physically removed until a pack() is
performedonthedatabase.
WhenaNumericfieldisdeclaredwithawidthof6and3decimalplaces,howmanydigitscan
existtotheleftofthedecimalwhenthefieldcontainsanegativevalue?
1:youmustallowonespaceforthedecimalandonespaceforthenegativesign.
Whendoyouneedtocreateafinalize()methodforyourclass?
Wheneveryouallocatesystemresourceslikefilesorphysicalmemorydirectlyinsteadofby
theJVMorusingaclasswhichalreadyprovidesafinalize()method.

WhatJavaclassprovidesthereadLine()methodtoobtain alineofinputfromatextfileor
stream?
BufferedReader
DoxBASEdatafilesprovideanybuiltinmethodofdataintegrity?
No.
Whatmustexist,nomatterhowthedataisstored,toprovidedataintegrity?
Anengineorotherservicethroughwhichalldataaccessisroutedwithoutexception.That
engineorserviceistheonlymethodofenforcingdatarules.

AnswerstoChapter2ReviewQuestions

WhatdoesCUAstandfor?
CommonUserAccess
WhatisthedefaultlookandfeelforJavaapplications?
Metal
WhatDBFmethodtellsyouifarecordhasbeendeleted?
deleted()
UnderwhatconditionsisitokaytoloadDBFcontentsintoaspreadsheet?
Whenthedataislocalandbeingloadedinreadonlymode.
AfteropeninganexistingDBFandattachingoneormoreexistingNDXfiles,whatshouldyou
do?
reIndex()
Are the various Field variable names required to match their corresponding xBASE column
names?
No
WhatinterfacemustaclassimplementinorderforittobeusedwithanArrays.sort()call?
Comparator

Doesthestatement:
MegaXDueElmsd_elms[]=newMegaXDueElms[ELM_COUNT];
completelyallocateanarrayofMegaXDueElmsordoyoustillhavetocreateeachindividual
element?
Create
Ifcreate,whatisthesyntaxtocreateanelement?
d_elms[i]=newMegaXDueElms();
WhatStringmethodmustbeusedwhenattemptingtoconverttheresultofaget()foraNumField
toanInteger()?why?
trim()
BecauseparseInt()cannothandlenonnumericcharacterslikespaces.
Whatjavaccommandlineoptionrestrictsthecontentofyoursourcecode?
source

This book was distributed courtesy of:

For your own Unlimited Reading and FREE eBooks today, visit:
http://www.Free-eBooks.net

Share this eBook with anyone and everyone automatically by selecting any of the
options below:

To show your appreciation to the author and help others have


wonderful reading experiences and find helpful information too,
we'd be very grateful if you'd kindly
post your comments for this book here.

COPYRIGHT INFORMATION
Free-eBooks.net respects the intellectual property of others. When a book's copyright owner submits their work to Free-eBooks.net, they are granting us permission to distribute such material. Unless
otherwise stated in this book, this permission is not passed onto others. As such, redistributing this book without the copyright owner's permission can constitute copyright infringement. If you
believe that your work has been used in a manner that constitutes copyright infringement, please follow our Notice and Procedure for Making Claims of Copyright Infringement as seen in our Terms
of Service here:

http://www.free-ebooks.net/tos.html

Anda mungkin juga menyukai