<!--- Kill extra output. ---> <cfsilent> <!--- Check to see which execution mode the tag is running in. In the start mode, we will param the tag attributes and start the loop. In the end mode, we will finish executing the loop. ---> <cfswitch expression="#THISTAG.ExecutionMode#"> <cfcase value="Start"> <!--- Param the tag attributes. ---> <!--- This is the CALLER-scoped variable into which we will store the contextual match. This may be a string or a struct depending on whether the user wants the Groups returned. ---> <cfparam name="ATTRIBUTES.Index" type="variablename" /> <!--- This is the text in which we will be searching for and iterating over the pattern matches. ---> <cfparam name="ATTRIBUTES.Text" type="string" /> <!--- This is the regular expression pattern that we will be matching. Since we using the Java regular expression engine, this goes by the java.util.regex.Pattern syntax, not necessarily by the ColdFusion regex syntax. ---> <cfparam name="ATTRIBUTES.Pattern" type="string" /> <!--- This is the CALLER-scoped variable into which the resulting string (after the patterns have been replaced) will be placed. If this value is not renamed, then no value will be passed back to the caller. ---> <cfparam name="ATTRIBUTES.Variable" type="variablename" default="undefined" /> <!--- This flags whether to return the single matched pattern or to return a structure with the set of groups returned. ---> <cfparam name="ATTRIBUTES.ReturnGroups" type="boolean" default="false" /> <!--- ASSERT: At this point, all of the tag attributes have been properly validated. ---> <!--- Set a short-hand flag to test for variable return (based on the current name). ---> <cfset THISTAG.HasReturn = (ATTRIBUTES.Variable NEQ "undefined") /> <!--- Create the compiled pattern object. ---> <cfset THISTAG.Pattern = CreateObject( "java", "java.util.regex.Pattern" ).Compile( JavaCast( "string", ATTRIBUTES.Pattern ) ) /> <!--- Get the pattern matcher for our target text from the compiled pattern. ---> <cfset THISTAG.Matcher = THISTAG.Pattern.Matcher( JavaCast( "string", ATTRIBUTES.Text ) ) /> <!--- Get the string buffer into which we will store the replaced text. However, we only care about this if the user wants the value back. ---> <cfif THISTAG.HasReturn> <!--- Create string buffer. ---> <cfset THISTAG.Buffer = CreateObject( "java", "java.lang.StringBuffer" ).Init() /> </cfif> <!--- Find the first pattern match. Since the Find() function returns a boolean flagging that a match was found, check to see if it returns true. ---> <cfif THISTAG.Matcher.Find()> <!--- Now that we found the first match, we need to check to see how the user wants the returned match - as a single string or as a group structure. ---> <cfif ATTRIBUTES.ReturnGroups> <!--- The user wants the group structure to be returned so create a structure to hold this data. ---> <cfset THISTAG.Index = StructNew() /> <!--- Store the number of matching groups in the regular expression. ---> <cfset THISTAG.Index.GroupCount = THISTAG.Matcher.GroupCount() /> <!--- Store the index of the match. Add one to get it to be a one-based index like ColdFusion. ---> <cfset THISTAG.Index.Start = (THISTAG.Matcher.Start() + 1) /> <!--- Store the entire match. ---> <cfset THISTAG.Index[ "0" ] = THISTAG.Matcher.Group() /> <!--- Loop over the matching groups and store them via indexes into the Index object. ---> <cfloop index="THISTAG.GroupIndex" from="1" to="#THISTAG.Index.GroupCount#" step="1"> <cfset THISTAG.Index[ "#THISTAG.GroupIndex#" ] = THISTAG.Matcher.Group( JavaCast( "int", THISTAG.GroupIndex ) ) /> </cfloop> <!--- Store new index object into the CALLER- scoped index variable. ---> <cfset "CALLER.#ATTRIBUTES.Index#" = THISTAG.Index /> <cfelse> <!--- The user just wants a string. Store the entire matching substring into the CALLER-scoped index variable. ---> <cfset "CALLER.#ATTRIBUTES.Index#" = THISTAG.Matcher.Group() /> </cfif> <cfelse> <!--- No matching pattern was found. We need to full exit the tag without doing any looping. However, we may need to store the value back into the caller. Check to see if we have a return value. ---> <cfif THISTAG.HasReturn> <!--- Just store the submitted text back into the CALLER scope. ---> <cfset "CALLER.#ATTRIBUTES.Variable#" = ATTRIBUTES.Text /> </cfif> <!--- Exit out of tag execution. ---> <cfexit method="exittag" /> </cfif> </cfcase> <cfcase value="End"> <!--- Now that the user has had a chance to update the matching group values, let's see if they are looking for a return. If they are, then we need to replace the groups back into the string buffer. ---> <cfif THISTAG.HasReturn> <!--- Now, we have to see how the user was viewing the groups (as a single string or a group structure). ---> <cfif ATTRIBUTES.ReturnGroups> <!--- When we are dealing with the group structure, we only care about the entire matched string (the zero group). Store that back into the Index so that we can deal with the replacement uniformly. ---> <cfset THISTAG.Index = CALLER[ ATTRIBUTES.Index ][ "0" ] /> <cfelse> <!--- Store the replacement string into the local Index so that we can deal with the replacement uniformly. ---> <cfset THISTAG.Index = CALLER[ ATTRIBUTES.Index ] /> </cfif> <!--- Replace the string into the buffer. When appending the replacement, be sure to escape any special characters. ---> <cfset THISTAG.Matcher.AppendReplacement( THISTAG.Buffer, JavaCast( "string", THISTAG.Index.ReplaceAll( JavaCast( "string", "([\\\$])" ), JavaCast( "string", "\$1" ) ) ) ) /> </cfif> <!--- ASSERT: At this point, we have dealt with all the changes that the user made to the matched patterns. Now, we can deal with the next match iteration. ---> <!--- Find the next pattern match. Since the Find() function returns a boolean flagging that a match was found, check to see if it returns true. ---> <cfif THISTAG.Matcher.Find()> <!--- Now that we found the next match, we need to check to see how the user wants the returned match - as a single string or as a group structure. ---> <cfif ATTRIBUTES.ReturnGroups> <!--- The user wants the group structure to be returned so create a structure to hold this data. ---> <cfset THISTAG.Index = StructNew() /> <!--- Store the number of matching groups in the regular expression. ---> <cfset THISTAG.Index.GroupCount = THISTAG.Matcher.GroupCount() /> <!--- Store the index of the match. Add one to get it to be a one-based index like ColdFusion. ---> <cfset THISTAG.Index.Start = (THISTAG.Matcher.Start() + 1) /> <!--- Store the entire match. ---> <cfset THISTAG.Index[ "0" ] = THISTAG.Matcher.Group() /> <!--- Loop over the matching groups and store them via indexes into the Index object. ---> <cfloop index="THISTAG.GroupIndex" from="1" to="#THISTAG.Index.GroupCount#" step="1"> <cfset THISTAG.Index[ "#THISTAG.GroupIndex#" ] = THISTAG.Matcher.Group( JavaCast( "int", THISTAG.GroupIndex ) ) /> </cfloop> <!--- Store new index object into the CALLER- scoped index variable. ---> <cfset "CALLER.#ATTRIBUTES.Index#" = THISTAG.Index /> <cfelse> <!--- The user just wants a string. Store the entire matching substring into the CALLER-scoped index variable. ---> <cfset "CALLER.#ATTRIBUTES.Index#" = THISTAG.Matcher.Group() /> </cfif> <!--- Exit the tag as a loop. ---> <cfexit method="loop" /> <cfelse> <!--- No matching pattern was found. Therefore, we have exhausted all of the matches in this string. We need to full exit the tag; however, we may need to store the value back into the caller. Check to see if we have a return value. ---> <cfif THISTAG.HasReturn> <!--- Append the rest of the target text to the string buffer. ---> <cfset THISTAG.Matcher.AppendTail( THISTAG.Buffer ) /> <!--- Convert the string buffer into a single string and store it back into the CALLER-scoped variable. ---> <cfset "CALLER.#ATTRIBUTES.Variable#" = THISTAG.Buffer.ToString() /> </cfif> <!--- Exit out of tag execution. ---> <cfexit method="exittag" /> </cfif> </cfcase> </cfswitch> </cfsilent>