!
! (c) Copyright 2010-2013, Sage Software Canada Ltd. (Ontario, Canada)
/*
* Build information:
! ** @Author Fred McGuirk
! ** This observer will scan the current source file (in the build process) and
! ** remove all lines with duplicate line numbers; if the source file does not
! ** have line numbers, this observer will do nothing.
*/
def class "del_dupe_lines"
like "EventManagerObserver"
! ** Private property to maintain the name of the temporary file that is created
! ** during the Pre-Process so that it can be deleted during the Post-Process.
local tempSourceFile$
! ** Override values for description / NotificationFlag
local theDescription$="Remove lines with duplicate line numbers"
local theNotificationFlag=_pvxConstants'_idePrePostProcess
! ** Internal method to scan source file and remove remark lines with duplicate line numbers
! ** @param theSrcFile The source file to be processed
! ** @param newSrcFile The modified version of the source file
! ** @returns A simple boolean (0=False, 1=True) to indicate if any modifications were made
function local processFile(theSrcFile$,newSrcFile$) processFile
/*
! ** The logic to be executed when the observer is triggered. This logic must
! ** check the major and minor codes to determine the current event and then
! ** decide what action is to be performed.
! **
! ** If this observer is set to watch both Pre-Process and Post-Process states
! ** for events, the logic in the 'update' method must check the state of the
! ** _pvxState'getArgument(_pvxConstants'_iEventNotificationFlag$) flag to
! ** determine the appropriate code to be executed.
! **
! ** @param state A reference to an object of class %PvxClass(PvxState)%
*/
function update(initPvxState) update
end def
/*
* The update routine will be executed by the event manager during the
* pre/post process logic depending on how this observer registers
* itself through the getEventNotification() method.
*/
update:
enter aPvxState
local oPvxUtility,psMajor$,psMinor$,source,tempSourceFile$,enfState,x
! Get the Major/Minor codes that identify the current action
psMajor$=aPvxState'getMajor$(), \
psMinor$=aPvxState'getMinor$()
/*
* This observer is for the program build event only; all other events
* should be ignored.
*/
if (psMajor$=_pvxConstants'Incremental_Build$ \
or psMajor$=_pvxConstants'Incremental_Build_Alt_Exe$) \
and psMinor$=_pvxConstants'BuildType_BuildOne$ {
! Check the current state of processing for the ProvideX Event Manager
enfState=aPvxState'getArgumentValue(_pvxConstants'_iEventNotificationFlag$)
switch enfState
case _pvxConstants'_idePreProcess
! Get the the source file associated with the current action
source$=aPvxState'getArgumentValue$(_pvxConstants'SrcFile$)
/*
* Get a temporary file to be used as new source
*/
oPvxUtility=new("pvx_utility")
tempSourceFile$=oPvxUtility'getWorkFile$("WRK_dup_lines")
drop object oPvxUtility
! Create a new temporary file
serial tempSourceFile$,err=*next
! Process this Source File to remove remarks and place modified version into the tempSourceFile$
isModified=_obj'processFile(source$,tempSourceFile$)
if isModified {
! Update task state information to override the source file
aPvxState'setArgument(_pvxConstants'SrcFile$,tempSourceFile$)
} else {
erase tempSourceFile$,err=*next
tempSourceFile$=""
}
break
case _pvxConstants'_idePostProcess
! erase the temporary source file
if not(nul(tempSourceFile$)) {
erase tempSourceFile$,err=*next
tempSourceFile$=""
}
break
end switch
}
return 0
processFile:
enter (theSourceFile$),(newSourceFile$),err=*next
local lastLine,lineFeeds,thisLine,outFN,oPvxUtility,pgmLine$,pr,retVal,srcText$,theType,wrkfile$
/* Now, to update the source file based on user preferences */
pr=new("program_reader",theSourceFile$,err=*next)
if pr {
theType=pr'getSourceFileType()
/*
* Skip any source file that is not a test file or does not have line numbers.
*/
if theType<>1 or not(pr'hasLineNumbers()) {
drop object pr; pr=0
}
}
if pr {
open lock (hfn,err=*next)newSourceFile$;
outFN=lfo
if outFN {
lastLine=0
oPvxUtility=new("pvx_utility")
/*
* Read through the source file and check:
* - source file is using line numbers
* - drop duplicate line number
*/
while not(pr'getEOF())
pgmLine$=pr'read$()
if not(pr'getEOF()) {
srcText$=pr'getSourceText$()
srcText$=oPvxUtility'stripBlockComments$(srcText$)
/*
* Do NOT replace non-ASCII characters to allow accented
* characters to remain in the final tokenized program; just
* remove known control characters
*/
srcText$=sub(srcText$,$09$," ",0) /* Replace all TAB characters */
srcText$=sub(srcText$,$0D$,"",0) /* Remove all CR characters */
/*
* Keep track of the number of LineFeeds in original source line;
* strip the trailing linefeeds - they will be added back on later
*/
lineFeeds=pos($0A$=srcText$,1,0)
srcText$=stp(srcText$,2,$0A$)
if nul(srcText$) {
srcText$=""
} else {
/*
* This logic will assume that the line number is either 4 or 5
* characters in length followed by a space and will therefore
* simply check the first five characters of the source line to
* get the line number. This logic can be easily changed to
* look for the first space in the line and then use the value
* before the space as the line number.
*/
thisLine=num(mid(srcText$,1,5),err=*break)
/*
* Check:
* - line number ZERO
* - duplicate line number
* If either condition is true:
* - Clear Source Text
* - Set return value to indicate source file has changed
*
* This logic can be easily enhanced to restrict the duplicate
* line number check to only REMARK lines, for exanple by
* capturing the position of the first directive in the source
* line and adding a check in the condition below.
*/
if thisLine<1 or thisLine=lastLine {
srcText$="", \
retVal=1
}
if thisLine>0 { lastLine=thisLine }
}
/*
* Add Line Feeds to END of source text to maintain line
* reference count with original source file
*/
srcText$+=dim(lineFeeds,$0A$)
write record(outFN)srcText$
}
wend
close (outFN)
drop object oPvxUtility; oPvxUtility=0
}
drop object pr; pr=0
}
return retVal
end