865282a Assorted cleanup and minor performance improvements.
- Parent c61e8f2e49d2c832dc689de763d4a6564de6ac85
- Authored by Peter Boughton at Sat 25 Jun 2011, 01:48
- tag: v0.7.4
Application.cfc | 3 +-
cfcs/jre-utils.cfc | 227 +++++++++++++++++---
cfcs/qpscanner.cfc | 70 +++---
3 files changed, 224 insertions(+), 76 deletions(-)
diff --git a/Application.cfc b/Application.cfc
index 857917f..3e8755a 100644
--- a/Application.cfc
+++ b/Application.cfc (view file)
@@ -23,8 +23,7 @@
<cffunction name="onRequestStart" returntype="Boolean" output="false">
<cfset var Result = True/>
- <!--- TODO: FIX: Implement as URL check once CFCs are stable. --->
- <cfif True>
+ <cfif StructKeyExists(Url,'AppReload')>
<cfset Result = Result AND onApplicationStart()/>
</cfif>
diff --git a/cfcs/jre-utils.cfc b/cfcs/jre-utils.cfc
index c2417c5..d651431 100644
--- a/cfcs/jre-utils.cfc
+++ b/cfcs/jre-utils.cfc (view file)
@@ -1,12 +1,13 @@
-<cfcomponent output="false" displayname="jre-utils v0.6">
+<cfcomponent output="false" displayname="jrex v0.8ish">
<cffunction name="init" output="false" access="public">
<cfargument name="DefaultFlags" type="String" default="MULTILINE"/>
- <cfargument name="IgnoreInvalidFlags" type="Boolean" default="false"/>
- <cfargument name="BackslashReferences" type="Boolean" default="false"/>
+ <cfargument name="IgnoreInvalidFlags" type="Boolean" default="false" />
+ <cfargument name="BackslashReferences" type="Boolean" default="false" />
+ <cfargument name="SetNullGroupsBlank" type="Boolean" default="true" />
- <cfset var CurrentFlag = ""/>
+ <cfset var CurProp = 0 />
<cfset This.Flags =
{ UNIX_LINES = 1
@@ -18,9 +19,16 @@
, CANON_EQ = 128
}/>
+
<cfset This.DefaultFlags = This.parseFlags( Arguments.DefaultFlags , Arguments.IgnoreInvalidFlags )/>
- <cfset This.BackslashReferences = Arguments.BackslashReferences/>
+ <cfloop index="CurProp" list="BackslashReferences,SetNullGroupsBlank">
+ <cfset This[CurProp] = Arguments[CurProp] />
+ </cfloop>
+
+
+ <cfset Variables.PatternCache = {} />
+
<!--- In CFMX we cannot define a "Replace" function directly. --->
<cfset This.replace = _replace/>
@@ -31,8 +39,25 @@
- <cffunction name="Struct" returntype="Struct" access="private"><cfreturn Arguments/></cffunction>
+ <cffunction name="onMissingMethod" returntype="any" output="false" access="public">
+ <cfargument name="MissingMethodName" type="String" />
+ <cfargument name="MissingMethodArguments" type="Struct" />
+ <cfif right(Arguments.MissingMethodName,6) EQ 'NOCASE'>
+ <cfset var TargetFunction = left(Arguments.MissingMethodName,len(Arguments.MissingMethodName)-6) />
+ <cfset var Args = Arguments.MissingMethodArguments />
+
+ <cfif StructKeyExists(Args,'Flags')>
+ <cfset Args.Flags = BitOr( Args.Flags , This.Flags.CASE_INSENSITIVE ) />
+ <cfelse>
+ <cfset Args.Flags = BitOr( This.DefaultFlags , This.Flags.CASE_INSENSITIVE ) />
+ </cfif>
+
+ <cfset var Function = This[TargetFunction] />
+ <cfreturn Function(ArgumentCollection=Args) />
+ </cfif>
+
+ </cffunction>
@@ -61,37 +86,126 @@
</cffunction>
+ <cffunction name="compilePattern" output="false" access="public">
+ <cfargument name="Regex" type="String" />
+ <cfargument name="Flags" type="String" />
- <cffunction name="matches" returntype="Boolean" output="false" access="public">
+ <cfset var Key = Hash(Arguments.Regex&Arguments.Flags) />
+
+ <cfif NOT StructKeyExists(Variables.PatternCache,Key)>
+ <cfset Variables.PatternCache[Key] = createObject("java","java.util.regex.Pattern")
+ .compile( Arguments.Regex , parseFlags(Arguments.Flags) ) />
+ </cfif>
+
+ <cfreturn Variables.PatternCache[Key]/>
+ </cffunction>
+
+
+
+ <cffunction name="flushPatternCache" returntype="void" outout="false" access="public"
+ hint="Clears cache for specified pattern, or all patterns if none specified.">
+ <cfargument name="Regex" type="String" required="false" />
+ <cfargument name="Flags" type="String" required="false" />
+
+ <cfif StructKeyExists(Arguments,'Regex')>
+ <cfparam name="Arguments.Flags" default="#This.DefaultFlags#"/>
+
+ <cfset StructDelete( Variables.PatternCache , Hash(serialize(Arguments)) ) />
+
+ <cfelseif StructKeyExists(Arguments,'Flags')>
+ <cfthrow
+ message = "Argument 'flags' can only be used in conjunction with argument 'regex'."
+ type = "JreUtils.FlushPatternCache.InvalidArgument.Flags"
+ />
+
+ <cfelse>
+ <cfset Variables.PatternCache = {} />
+
+ </cfif>
+
+ </cffunction>
+
+
+
+
+ <cffunction name="get" returntype="Array" output="false" access="public">
<cfargument name="Text" type="String"/>
<cfargument name="Regex" type="String"/>
<cfargument name="Flags" default="#This.DefaultFlags#"/>
- <cfset var Pattern = createObject("java","java.util.regex.Pattern")
- .compile( Arguments.Regex , parseFlags(Arguments.Flags) )/>
+ <cfset var Pattern = compilePattern( Arguments.Regex , Arguments.Flags )/>
<cfset var Matcher = Pattern.Matcher(Arguments.Text)/>
+ <cfset var Matches = ArrayNew(1)/>
<cfloop condition="Matcher.find()">
- <cfreturn True/>
+ <cfset ArrayAppend(Matches,Matcher.Group())/>
</cfloop>
- <cfreturn False/>
+ <cfreturn Matches/>
</cffunction>
- <cffunction name="get" returntype="Array" output="false" access="public">
+ <cffunction name="getFirst" returntype="String" output="false" access="public">
+ <cfargument name="Text" type="String"/>
+ <cfargument name="Regex" type="String"/>
+ <cfargument name="Flags" default="#This.DefaultFlags#"/>
+
+ <cfset var Pattern = compilePattern( Arguments.Regex , Arguments.Flags )/>
+ <cfset var Matcher = Pattern.Matcher(Arguments.Text)/>
+
+ <cfif Matcher.find()>
+ <cfreturn Matcher.Group() />
+ <cfelse>
+ <cfreturn '' />
+ </cfif>
+ </cffunction>
+
+
+
+ <cffunction name="getCount" returntype="Numeric" output="false" access="public">
+ <cfargument name="Text" type="String"/>
+ <cfargument name="Regex" type="String"/>
+ <cfargument name="Flags" default="#This.DefaultFlags#"/>
+
+ <cfset var Pattern = compilePattern( Arguments.Regex , Arguments.Flags )/>
+ <cfset var Matcher = Pattern.Matcher(Arguments.Text)/>
+ <cfset var Count = 0 />
+
+ <cfloop condition="Matcher.find()">
+ <cfset Count++ />
+ </cfloop>
+
+ <cfreturn Count />
+ </cffunction>
+
+
+
+ <cffunction name="getGroups" returntype="Array" output="false" access="public">
<cfargument name="Text" type="String"/>
<cfargument name="Regex" type="String"/>
+ <cfargument name="SetNullGroupsBlank" type="Boolean" default="#This.SetNullGroupsBlank#"/>
<cfargument name="Flags" default="#This.DefaultFlags#"/>
- <cfset var Pattern = CreateObject("java","java.util.regex.Pattern")
- .compile( Arguments.Regex , parseFlags(Arguments.Flags) )/>
+ <cfset var Pattern = compilePattern( Arguments.Regex , Arguments.Flags )/>
<cfset var Matcher = Pattern.Matcher(Arguments.Text)/>
<cfset var Matches = ArrayNew(1)/>
+
<cfloop condition="Matcher.find()">
- <cfset ArrayAppend(Matches,Matcher.Group())/>
+ <cfset CurMatch =
+ { match = Matcher.Group()
+ , groups = ArrayNew(1)
+ }/>
+ <cfloop index="i" from="1" to="#Matcher.groupCount()#">
+ <cfif (Matcher.start(i) EQ -1) AND Arguments.SetNullGroupsBlank >
+ <cfset ArrayAppend(CurMatch.Groups,'')/>
+ <cfelse>
+ <cfset ArrayAppend(CurMatch.Groups,Matcher.group(i))/>
+ </cfif>
+ </cfloop>
+
+ <cfset ArrayAppend(Matches,CurMatch)/>
</cfloop>
<cfreturn Matches/>
@@ -99,19 +213,69 @@
- <cffunction name="getNoCase" returntype="Array" output="false" access="public">
+
+ <!--- \ match* - clones of get* with first two arguments swapped. --->
+
+ <cffunction name="match" returntype="Array" output="false" access="public"
+ hint="This function swaps argument order for consistency with rematch">
+ <cfargument name="Regex" type="String"/>
+ <cfargument name="Text" type="String"/>
+ <cfargument name="Flags" default="#This.DefaultFlags#"/>
+
+ <cfreturn This.get( ArgumentCollection = Arguments )/>
+ </cffunction>
+
+
+
+ <cffunction name="matchFirst" returntype="String" output="false" access="public">
+ <cfargument name="Regex" type="String"/>
+ <cfargument name="Text" type="String"/>
+ <cfargument name="Flags" default="#This.DefaultFlags#"/>
+
+ <cfreturn This.getFirst( ArgumentCollection = Arguments )/>
+ </cffunction>
+
+
+
+ <cffunction name="matchCount" returntype="String" output="false" access="public">
+ <cfargument name="Regex" type="String"/>
<cfargument name="Text" type="String"/>
+ <cfargument name="Flags" default="#This.DefaultFlags#"/>
+
+ <cfreturn This.getCount( ArgumentCollection = Arguments )/>
+ </cffunction>
+
+
+
+ <cffunction name="matchGroups" returntype="Array" output="false" access="public">
<cfargument name="Regex" type="String"/>
+ <cfargument name="Text" type="String"/>
+ <cfargument name="SetNullGroupsBlank" type="Boolean" default="#This.SetNullGroupsBlank#"/>
<cfargument name="Flags" default="#This.DefaultFlags#"/>
- <cfreturn This.get
- ( Text : Arguments.Text
- , Regex : Arguments.Regex
- , Flags : BitOr( Arguments.Flags , This.Flags.CASE_INSENSITIVE )
- )/>
+ <cfreturn This.getGroups( ArgumentCollection = Arguments ) />
</cffunction>
+ <!--- / match* --->
+
+
+
+ <cffunction name="matches" returntype="Boolean" output="false" access="public">
+ <cfargument name="Text" type="String"/>
+ <cfargument name="Regex" type="String"/>
+ <cfargument name="Flags" default="#This.DefaultFlags#"/>
+
+ <cfset var Pattern = compilePattern( Arguments.Regex , Arguments.Flags )/>
+ <cfset var Matcher = Pattern.Matcher(Arguments.Text)/>
+
+ <cfloop condition="Matcher.find()">
+ <cfreturn true/>
+ </cfloop>
+
+ <cfreturn false/>
+ </cffunction>
+
<cffunction name="_replace" returntype="String" output="false" access="public">
@@ -119,6 +283,7 @@
<cfargument name="Regex" type="String"/>
<cfargument name="Replacement" type="Any" hint="String or UDF"/>
<cfargument name="Scope" type="String" default="ONE" hint="ONE,ALL"/>
+ <cfargument name="Flags" type="String" default="#This.DefaultFlags#"/>
<cfset var String = ""/>
<cfset var Pattern = ""/>
@@ -144,7 +309,7 @@
<cfelse>
- <cfset Pattern = createObject("java","java.util.regex.Pattern").compile(Arguments.Regex)/>
+ <cfset Pattern = compilePattern( Arguments.Regex , Arguments.Flags )/>
<cfset Matcher = Pattern.Matcher( Arguments.Text )/>
<cfset Results = createObject("java","java.lang.StringBuffer").init()/>
@@ -173,20 +338,16 @@
- <cffunction name="escape" returntype="String" output="false" access="public">
- <cfargument name="Text" type="String"/>
- <cfset var Result = Arguments.Text/>
- <cfset var Symbol = ""/>
- <cfset var EscapeChars = "\,.,[,],(,),^,$,|,?,*,+,{,}"/>
-
- <cfloop index="Symbol" list="#EscapeChars#">
- <cfset Result = replace( Result , Symbol , '\'&Symbol , 'all')/>
- </cfloop>
+ <cffunction name="split" returntype="Array" output="false" access="public">
+ <cfargument name="Text" type="String"/>
+ <cfargument name="Regex" type="String"/>
+ <cfargument name="Flags" default="#This.DefaultFlags#"/>
- <cfreturn Result />
+ <cfreturn compilePattern( Arguments.Regex , Arguments.Flags )
+ .split(Arguments.Text)
+ />
</cffunction>
-
</cfcomponent>
\ No newline at end of file
diff --git a/cfcs/qpscanner.cfc b/cfcs/qpscanner.cfc
index 8e8e6c9..d5fc334 100644
--- a/cfcs/qpscanner.cfc
+++ b/cfcs/qpscanner.cfc (view file)
@@ -44,17 +44,15 @@
<cfset Variables.AlertData = QueryNew(Variables.ResultFields)/>
<cfsavecontent variable="RegexList"><cfoutput>
- findQueries |(?si)(<#cf#query[^p]).*?(?=</#cf#query>)
- findQueryTag |(?si)(<#cf#query(?!p)[^>]{0,300}>)
+ findQueries |(?si)(<#cf#query\b)(?:[^<]++|<(?!/#cf#query>))+(?=</#cf#query>)
+ findQueryTag |(?si)(<#cf#query[^p][^>]++>)
isQueryOfQuery |(?si)dbtype\s*=\s*["']query["']
- killParams |(?si)<#cf#queryparam[^>]+>
- killCfTag |(?si)<#cf#[a-z]{2,}[^>]*> <!--- Deliberately excludes Custom Tags and CFX --->
+ killParams |(?si)<#cf#queryparam[^>]++>
+ killCfTag |(?si)<#cf#[a-z]{2,}[^>]*+> <!--- Deliberately excludes Custom Tags and CFX --->
killOrderBy |(?si)\bORDER BY\b.*?$
killBuiltIn |(?si)##(#ListChangeDelims(This.BuiltInFunctions,'|')#)\([^)]*\)##
findScopes |(?si)(?<=##([a-z]{1,20}\()?)[^\(##<]+?(?=\.[^##<]+?##)
- findName |(?si)(?<=(<#cf#query[^>]{0,300}\bname=")).*?(?="[^>]{0,300}>)
findClientScopes |(?i)\b(#ListChangeDelims(This.ClientScopes,'|')#)\b
- isCfmlFile |(?i)\.cf(c|ml?)$
</cfoutput></cfsavecontent>
<cfloop index="Rex" list="#RegexList#" delimiters="#Chr(10)#">
@@ -108,6 +106,7 @@
<cfset var CurrentTarget = -1/>
<cfset var process = true/>
<cfset var jre = Variables.jre/>
+ <cfset var Ext = 0 />
<cfif DirectoryExists(Arguments.DirName)>
@@ -136,13 +135,19 @@
<cfset scan( CurrentTarget )/>
- <cfelseif jre.matches( CurrentTarget , Variables.Regexes.isCfmlFile )>
- <cfset This.Totals.FileCount = This.Totals.FileCount + 1 />
-
- <cfset qryCurData = hunt( CurrentTarget )/>
+ <cfelse>
+ <cfset Ext = LCase(ListLast(CurrentTarget,'.')) >
+
+ <cfif Ext EQ 'cfc' OR Ext EQ 'cfm' OR Ext EQ 'cfml'>
+
+ <cfset This.Totals.FileCount = This.Totals.FileCount + 1 />
+
+ <cfset qryCurData = hunt( CurrentTarget )/>
+
+ <cfif qryCurData.RecordCount>
+ <cfset Variables.AlertData = QueryAppend( Variables.AlertData , qryCurData )/>
+ </cfif>
- <cfif qryCurData.RecordCount>
- <cfset Variables.AlertData = QueryAppend( Variables.AlertData , qryCurData )/>
</cfif>
</cfif>
@@ -219,33 +224,37 @@
<cfset qryResult.QueryCode[CurRow] = jre.replace( QueryCode , Chr(13) , Chr(10) , 'all' ) />
<cfset qryResult.QueryCode[CurRow] = jre.replace( qryResult.QueryCode[CurRow] , Chr(10)&Chr(10) , Chr(10) , 'all' ) />
<cfif This.showScopeInfo >
- <cfset qryResult.ScopeList[CurRow] = ArrayToList( ArrayUnique( jre.get( rekCode , REX.findScopes ) ) ) />
+ <cfset qryResult.ScopeList[CurRow] = [] />
+ <cfloop index="CurScope" array="#jre.get( rekCode , REX.findScopes )#">
+ <cfif NOT ArrayFind(qryResult.ScopeList[CurRow],CurScope)>
+ <cfset ArrayAppend(qryResult.ScopeList[CurRow],CurScope)>
+ </cfif>
+ </cfloop>
<cfset qryResult.ContainsClientScope[CurRow] = false/>
<cfif This.highlightClientScopes>
<cfloop index="CurrentScope" list="#This.ClientScopes#">
- <cfif ListFind( qryResult.ScopeList[CurRow] , CurrentScope )>
+ <cfif ArrayFind( qryResult.ScopeList[CurRow] , CurrentScope )>
<cfset qryResult.ContainsClientScope[CurRow] = true/>
<cfbreak/>
</cfif>
</cfloop>
</cfif>
+
+ <cfset qryResult.ScopeList[CurRow] = ArrayToList(qryResult.ScopeList[CurRow]) />
</cfif>
- <!--- CF8 doesn't support get()[1] so need to use two lines: --->
- <cfset QueryTagCode = jre.get( Matches[i] , REX.findQueryTag )/>
- <cfset QueryTagCode = QueryTagCode[1] />
+ <cfset QueryTagCode = jre.getFirst( Matches[i] , REX.findQueryTag )/>
<cfset BeforeQueryCode = ListFirst ( replace ( ' '&FileData&' ' , Matches[i] , UniqueToken ) , UniqueToken )/>
-
<cfset StartLine = 1+ArrayLen( jre.get( BeforeQueryCode , chr(10) ) )/>
<cfset LineCount = ArrayLen( jre.get( Matches[i] , chr(10) ) )/>
<cfset qryResult.QueryStartLine[CurRow] = StartLine/>
<cfset qryResult.QueryEndLine[CurRow] = StartLine + LineCount />
- <cfset qryResult.QueryName[CurRow] = ArrayToList( jre.get( ListLast(QueryTagCode,chr(10)) , REX.findName ) )/>
+ <cfset qryResult.QueryName[CurRow] = jre.getFirst(QueryTagCode,'(?<=\bname\s{0,10}=\s{0,10}(["'']))\S(?=\1)') />
<cfset qryResult.QueryId[CurRow] = createUuid() />
<cfif NOT Len( qryResult.QueryName[CurRow] )>
<cfset qryResult.QueryName[CurRow] = "[unknown]"/>
@@ -268,32 +277,11 @@
</cffunction>
-
-
-
-
-
-
- <cffunction name="ArrayUnique" returntype="Array" output="false" access="private">
- <cfargument name="ArrayVar" type="Array"/>
- <cfset var UniqueToken = Chr(65536)/>
- <cfset var Result = duplicate(Arguments.ArrayVar)/>
- <cfset ArraySort(Result,'text')/>
- <cfset Result = ArrayToList( Result , UniqueToken )/>
- <!--- TODO: MINOR: FIX: Using \b works for the ScopeList, but is not good enough for general use - why not using UniqueToken? --->
- <cfset Result = REreplace( Result & UniqueToken , '(\b(.*?)\b)\1+' , '\1' , 'all' )/>
- <cfset Result = ListToArray( Result , UniqueToken )/>
- <!--- TODO: MINOR: Ideally, the original array order should be restored. --->
- <cfreturn Result/>
- </cffunction>
-
-
-
<cffunction name="QueryAppend" returntype="Query" output="false" access="private">
<cfargument name="QueryOne" type="Query"/>
<cfargument name="QueryTwo" type="Query"/>
<cfset var Result = -1/>
- <!--- Bug fix for CF8 --->
+ <!--- Bug fix for CF9 --->
<cfif NOT Arguments.QueryOne.RecordCount><cfreturn Arguments.QueryTwo /></cfif>
<cfif NOT Arguments.QueryTwo.RecordCount><cfreturn Arguments.QueryOne /></cfif>
<!--- / --->