cObject cParseFile cSingleLineParseFile cCSVParseFile cSDFParseFile cMultiLineParseFile cEMailParseFileThe cParseFile class has several abstract methods CreateCursor(), Import(), VerifyImportStructure() and VerifyRawData(). It also has concrete methods Scatter(), DestroyCursor() and ReadImportFile(). The concrete methods take on two roles: 1) doing the grunt work every subclass needs, 2) making calls to the abstract methods in the correct order making them essentially controller methods. The DestroyCursor() method:
* close the cusor created by CreateCursor if ( used( (this.cCursorName) ) ) use in (this.cCursorName) endifCan easily get rid of the cursor created by CreateCursor(). There's no dodefault() call because this is the class that defines this method.
local lcFile, llRetVal with this .cFilename = .parent.cFilename select (.cCursorName) llRetVal = .f. if ( .CheckPreviousImport() ) lcFile = .GetImportFile() .cProgressTitle = 'Reading ' + lcFile if ( ! empty( lcFile ) ) if ( .VerifyImportFileStructure( lcFile ) ) .ReadImportFileToCursor( lcFile ) goto top llRetVal = .VerifyRawData() if ( .lCheckPreviousImport ) select count(*) ; from (.cCursorName) ; into array laJunk .parent.parent.nRawRec = laJunk[1] endif else .cErrorStatus = "File structure is bad:" + lcFile endif endif endif endwith return llRetValIs not designed (or desirable) to be overridden, There is lots of conditional code in these kind of methods which makes it a bitch to override and know where to dodefault() to it. To make these controller methods workable for subclasses though plug in abstract/concrete method calls are made: CheckPreviousImport(), GetImportFile(), VerifyImportFileStructure() and VerifyRawData(). These allow the subclasses to impliment the VerifyImportFileStructure method which is vastly different between a CSV and a SDF file for example. These methods are provided by the cCSVParseFile and cSDFParseFile classes, and the instance level doesn't need any code there. Finding the right plug in points sometimes takes actually building a couple of subclasses to find deficiencies in these controller methods. I'm not sure all of the design time in the world would necessarily help some of this is just an evolutionary/iterative process, at least it is for me.
lparameter lcFile dodefault() this.cProgressTitle = "Loading file " + lcFile append from (lcFile) type sdf goto top if ( this.nHeaderLines > 0 ) * get rid of header lines delete next this.nHeaderLines endifDoes do a dodefault() because the method is defined two levels up the class tree and even though there's no code higher up right now, it doesn't mean that there won't be code there someday.