- qpscanner/cfcs/cfregex.cfc
- v0.7.5
- 23 KB
- 673
1<cfcomponent output=false >
2
3 <!---
4 NOTE: This is a dual-purpose Object CFC and CustomTag CFC
5 --->
6
7
8 <cffunction name="init" returntype="any" output=false>
9 <cfset StructDelete(This,'init') />
10
11 <cfset Variables.Modes =
12 { UNIX_LINES = 1
13 , CASE_INSENSITIVE = 2
14 , COMMENTS = 4
15 , MULTILINE = 8
16 , DOTALL = 32
17 , UNICODE_CASE = 64
18 , CANON_EQ = 128
19 }/>
20
21 <!--- TODO: Get defaults from admin settings. --->
22 <!--- If custom tag, default COMMENTS on, otherwise no defaults. --->
23 <cfset Variables.DefaultModes = Variables.Modes['COMMENTS'] * StructKeyExists(Arguments,'HasEndTag') />
24
25 <!--- INFO: If FuncName is escape or quote, don't compile regex, just return object. --->
26 <cfif StructKeyExists(Arguments,'FuncName') AND ListFindNoCase('escape,quote',Arguments.FuncName)>
27 <cfif StructKeyExists(Arguments,'Text') >
28 <cfset Variables.PatternText = Arguments.Text />
29 <cfelse>
30 <cfset Variables.PatternText = Arguments.Pattern />
31 </cfif>
32 <cfif NOT StructKeyExists(Arguments,'Modes')>
33 <cfset Arguments.Modes = Variables.DefaultModes />
34 </cfif>
35 <cfset Variables.ActiveModes = parseModes(Arguments.Modes) />
36 <cfreturn This />
37 </cfif>
38
39 <!--- INFO: If not tag, pass to .compile(...) --->
40 <cfif NOT StructKeyExists(Arguments,'HasEndTag')>
41 <cfreturn This.compile(ArgumentCollection=Arguments) />
42 </cfif>
43
44 <cfif NOT Arguments.HasEndTag >
45 <cfthrow
46 message = "The cfregex tag must have a closing tag."
47 type = "cfRegex.Tag.MissingEndTag"
48 />
49 </cfif>
50
51 </cffunction>
52
53
54 <!---
55 \\\ TAG FUNCS \\\
56 --->
57
58 <cffunction name="onStartTag" returntype="boolean" output=false >
59 <cfargument name="Attributes" type="Struct" required="true" />
60 <cfargument name="Caller" type="Struct" required="true" />
61
62 <cfreturn true />
63 </cffunction>
64
65
66 <cffunction name="onEndTag" returntype="boolean" output=false >
67 <cfargument name="Attributes" type="Struct" required="true" />
68 <cfargument name="Caller" type="Struct" required="true" />
69 <cfargument name="GeneratedContent" type="String" required="true" />
70
71 <cfif StructKeyExists(Arguments.Attributes,'Action')>
72
73 <!--- TODO: Consider Modifiers.
74 ~NoCase
75 ~First
76 --->
77
78 <cfif StructKeyExists(This,Arguments.Attributes.Action)
79 AND StructKeyExists(getMetaData(This[Arguments.Attributes.Action]),'Action')
80 >
81 <!--- TODO: Validate. --->
82 <cfelse>
83 <cfthrow
84 message = "Invalid Action of '#Arguments.Attributes.Action#'"
85 detail = "Please see cfRegex documentation for valid Action values."
86 type = "cfRegex.Tag.InvalidAction"
87 />
88 </cfif>
89 <cfelseif StructKeyExists(Arguments.Attributes,'Text')>
90 <!--- Input Text exists - check main actions. --->
91 <cfset var CurAction = "" />
92 <cfloop index="CurAction" list="#StructKeyList(This)#">
93 <cfif ListFindNoCase('onEndTag,onStartTag',CurAction)><cfcontinue /></cfif>
94 <cfif StructKeyExists(getMetaData(This[CurAction]),'Action') AND StructKeyExists(Arguments.Attributes,CurAction)>
95 <cfset Arguments.Attributes.Action = CurAction />
96 </cfif>
97 </cfloop>
98 <cfif NOT StructKeyExists(Arguments.Attributes,'Action')>
99 <cfthrow
100 message = "No action specified, unable to detect correct action."
101 detail = "Please see cfRegex documentation for valid Action values."
102 type = "cfRegex.Tag.UnknownAction"
103 />
104 </cfif>
105 <cfelseif StructKeyExists(Arguments.Attributes,'escape')>
106 <cfset Arguments.Attributes.Action = "Escape" />
107 <cfelseif StructKeyExists(Arguments.Attributes,'quote')>
108 <cfset Arguments.Attributes.Action = "Quote" />
109 <cfelse>
110 <cfset Arguments.Attributes.Action = 'Compile' />
111 </cfif>
112
113
114 <cfif NOT StructKeyExists(Arguments.Attributes,'Pattern')>
115 <cfset Arguments.Attributes.Pattern = Arguments.GeneratedContent />
116 </cfif>
117 <cfif NOT StructKeyExists(Arguments.Attributes,'Modes')>
118 <cfset Arguments.Attributes.Modes = Variables.DefaultModes />
119 </cfif>
120
121 <cfif Arguments.Attributes.Action NEQ 'Compile'>
122 <cfset compilePattern(ArgumentCollection=Arguments.Attributes) />
123 </cfif>
124
125 <cfset var Result = This[Arguments.Attributes.Action] />
126 <cfset Result = Result(ArgumentCollection=Arguments.Attributes) />
127
128 <cfif StructKeyExists(Arguments.Attributes,'Name')>
129 <cfset SetVariable("Caller.#Arguments.Attributes.Name#",Result) />
130 <cfelseif StructKeyExists(Arguments.Attributes,'Variable')>
131 <cfset SetVariable("Caller.#Arguments.Attributes.Variable#",Result) />
132 <cfelse>
133 <cfset SetVariable("Caller.cfregex",Result) />
134 </cfif>
135
136 <cfreturn false />
137 </cffunction>
138
139 <!---
140 /// TAG FUNCS ///
141 --->
142
143 <!---
144 \\\ INTERNAL \\\
145 --->
146
147 <cffunction name="parseModes" returntype="Numeric" output="false" access="private">
148 <cfargument name="ModeList" type="String" required="true" />
149 <cfargument name="IgnoreInvalidModes" type="Boolean" default="false"/>
150 <cfset var CurrentMode = ""/>
151 <cfset var ResultMode = 0/>
152
153 <cfloop index="CurrentMode" list="#Arguments.ModeList#">
154
155 <cfif isNumeric(CurrentMode)>
156 <cfset ResultMode = BitOr( ResultMode , CurrentMode )/>
157
158 <cfelseif StructKeyExists( Variables.Modes , CurrentMode )>
159 <cfset ResultMode = BitOr( ResultMode , Variables.Modes[CurrentMode] )/>
160
161 <cfelseif NOT Arguments.IgnoreInvalidModes>
162 <cfthrow
163 message = "Invalid Mode!"
164 detail = "Mode [#CurrentMode#] is not supported."
165 type = "cfRegex.Compile.InvalidMode"
166 />
167
168 </cfif>
169
170 </cfloop>
171
172 <cfreturn ResultMode />
173 </cffunction>
174
175
176 <cffunction name="compilePattern" returntype="void" output="false" access="private">
177 <cfargument name="Pattern" type="String" required="true" />
178 <cfargument name="Modes" type="String" required="true" />
179
180 <cfset Variables.PatternText = Arguments.Pattern />
181
182 <cfset Variables.ActiveModes = parseModes(Arguments.Modes) />
183
184 <cfset Variables.PatternObject = createObject("java","java.util.regex.Pattern")
185 .compile( Arguments.Pattern , Variables.ActiveModes ) />
186
187 </cffunction>
188
189
190 <cffunction name="buildMatchInfo" returntype="Struct" output="false" access="private">
191 <cfargument name="Matcher" type="any" required="true" />
192 <cfargument name="PosOffset" type="Numeric" optional />
193 <cfargument name="GroupNames" type="any" optional />
194
195 <cfset var MatchInfo =
196 { Match = Matcher.group()
197 , Groups = []
198 } />
199
200 <cfif StructKeyExists(Arguments,'PosOffset')>
201 <cfset MatchInfo.Pos = Arguments.PosOffset+Matcher.start() />
202 <cfset MatchInfo.Len = Matcher.end()-Matcher.start() />
203 </cfif>
204
205 <cfset var CurGroup = 0 />
206 <cfloop index="CurGroup" from=1 to=#Matcher.groupCount()#>
207 <cfif StructKeyExists(Arguments,'PosOffset')>
208 <cfset MatchInfo.Groups[CurGroup] =
209 { Pos = Arguments.PosOffset+Matcher.start(CurGroup)
210 , Len = Matcher.end(CurGroup)-Matcher.start(CurGroup)
211 , Match = Matcher.group(JavaCast('int',CurGroup))
212 } />
213 <cfelse>
214 <cfset MatchInfo.Groups[CurGroup] = Matcher.group(JavaCast('int',CurGroup)) />
215 </cfif>
216 </cfloop>
217
218 <cfif StructKeyExists(Arguments,'GroupNames')>
219 <cfif isSimpleValue(Arguments.GroupNames)>
220 <cfset Arguments.GroupNames = ListToArray(Arguments.GroupNames) />
221 </cfif>
222 <cfif ArrayLen(Arguments.GroupNames)>
223 <cfset var i = 0 />
224 <cfset MatchInfo.NamedGroups = {} />
225 <cfloop index="i" from="1" to="#Min(ArrayLen(Arguments.GroupNames),ArrayLen(MatchInfo.Groups))#">
226 <cfset MatchInfo.NamedGroups[ Arguments.GroupNames[i] ] = MatchInfo.Groups[i] />
227 </cfloop>
228 </cfif>
229 </cfif>
230
231 <cfreturn MatchInfo />
232 </cffunction>
233
234
235 <!---
236 /// INTERNAL ///
237 --->
238
239
240 <cffunction name="compile" returntype="cfRegex" output="false" access="public" action>
241 <cfargument name="Pattern" type="String" required="true" />
242 <cfargument name="Modes" type="String" default="#Variables.DefaultModes#" />
243 <cfset StructDelete(This,'compile') />
244 <cfset StructDelete(This,'onStartTag') />
245 <cfset StructDelete(This,'onEndTag') />
246
247 <cfset compilePattern(ArgumentCollection=Arguments) />
248
249 <cfreturn this />
250 </cffunction>
251
252 <!---
253 \\\ EXTERNAL \\\
254 --->
255
256 <cffunction name="find" returntype="Array" output="false" access="public" action>
257 <cfargument name="Text" type="String" required="true" />
258 <cfargument name="Start" type="Numeric" default=1 />
259 <cfargument name="Limit" type="Numeric" default=0 />
260 <cfargument name="ReturnType" type="String" default="pos" />
261
262 <cfif NOT ListFindNoCase('pos,sub,info',Arguments.ReturnType)>
263 <cfthrow message="Unknown returntype" />
264 </cfif>
265
266 <cfset var Offset = Max(1,Arguments.Start) />
267 <cfif Offset GT 1>
268 <cfset Arguments.Text = mid(Arguments.Text,Offset,Len(Arguments.Text)) />
269 </cfif>
270
271 <cfset var Matcher = Variables.PatternObject.Matcher(Arguments.Text) />
272 <cfset var Results = [] />
273
274 <cfloop condition="Matcher.find()">
275 <cfswitch expression=#LCase(Arguments.ReturnType)#>
276 <cfcase value="pos">
277 <cfset var CurMatch = Offset+Matcher.start() />
278 </cfcase>
279 <cfcase value="sub">
280 <cfset var CurMatch =
281 { pos = [Offset+Matcher.start()]
282 , len = [Matcher.end()-Matcher.start()]
283 } />
284 <cfloop index="local.CurGroup" from=1 to=#Matcher.groupCount()#>
285 <cfset ArrayAppend(CurMatch.pos,Offset+Matcher.start(CurGroup)) />
286 <cfset ArrayAppend(CurMatch.len,Matcher.end(CurGroup)-Matcher.start(CurGroup)) />
287 </cfloop>
288 </cfcase>
289 <cfcase value="info">
290 <cfset var CurMatch = buildMatchInfo(Matcher,Offset) />
291 </cfcase>
292 </cfswitch>
293 <cfset ArrayAppend( Results , CurMatch ) />
294
295 <cfif ArrayLen(Results) EQ Arguments.Limit>
296 <cfbreak />
297 </cfif>
298 </cfloop>
299
300 <cfreturn Results />
301 </cffunction>
302
303
304 <cffunction name="match" returntype="Array" output="false" access="public" action>
305 <cfargument name="Text" type="String" required="true" />
306 <cfargument name="Start" type="Numeric" optional />
307 <cfargument name="Limit" type="Numeric" default=0 />
308 <cfargument name="ReturnType" type="String" default="match" hint="match|groups|namedgroups|full" />
309 <cfargument name="GroupNames" type="any" default="" hint="Required if returnType=NamedGroup." />
310 <cfargument name="Callback" type="any" optional hint="Function called to determine if a match is included in results." />
311 <cfargument name="CallbackData" type="Struct" optional hint="Extra data which is passed in to callback function." />
312
313 <cfif NOT ListFindNoCase('match,groups,namedgroups,full',Arguments.ReturnType)>
314 <cfthrow message="Unknown returntype" />
315 </cfif>
316
317 <cfset var Offset = 1 />
318 <cfif StructKeyExists(Arguments,'Start') AND Arguments.Start>
319 <cfset Arguments.Text = mid(Arguments.Text,Arguments.Start,Len(Arguments.Text)) />
320 <cfset Offset = Arguments.Start+1 />
321 </cfif>
322
323 <cfset var Matcher = Variables.PatternObject.Matcher(Arguments.Text) />
324 <cfset var Results = [] />
325
326 <cfif StructKeyExists(Arguments,'GroupNames') AND isSimpleValue(Arguments.GroupNames)>
327 <cfset Arguments.GroupNames = ListToArray(Arguments.GroupNames) />
328 </cfif>
329
330 <cfloop condition="Matcher.find()">
331
332 <cfif StructKeyExists(Arguments,'Callback')>
333 <cfif NOT StructKeyExists(Arguments,'CallbackData')>
334 <cfset Arguments.CallbackData = {} />
335 </cfif>
336 <cfif NOT Arguments.Callback( ArgumentCollection=buildMatchInfo(Matcher,Offset,Arguments.GroupNames) , Data=Arguments.CallbackData )>
337 <cfcontinue />
338 </cfif>
339 </cfif>
340
341 <cfswitch expression=#Arguments.ReturnType#>
342 <cfcase value="match">
343 <cfset var CurMatch = Matcher.Group() />
344 </cfcase>
345 <cfcase value="groups">
346 <cfset var CurMatch = [] />
347 <cfloop index="local.CurGroup" from=1 to=#Matcher.groupCount()#>
348 <cfset CurMatch[CurGroup] = Matcher.group(JavaCast('int',CurGroup)) />
349 </cfloop>
350 </cfcase>
351 <cfcase value="namedgroups">
352 <cfset var CurMatch = {} />
353 <cfloop index="local.CurGroup" from=1 to=#Matcher.groupCount()#>
354 <cfset CurMatch[Arguments.GroupNames[CurGroup]] = Matcher.group(JavaCast('int',CurGroup)) />
355 </cfloop>
356 </cfcase>
357 <cfcase value="full">
358 <cfset var CurMatch = buildMatchInfo(Matcher=Matcher,GroupNames=Arguments.GroupNames) />
359 </cfcase>
360 </cfswitch>
361
362 <cfset ArrayAppend( Results , CurMatch ) />
363
364 <cfif ArrayLen(Results) EQ Arguments.Limit>
365 <cfbreak />
366 </cfif>
367 </cfloop>
368
369 <cfreturn Results />
370 </cffunction>
371
372
373 <cffunction name="matches" returntype="any" output="false" access="public" action>
374 <cfargument name="Text" type="String" required="true" />
375 <cfargument name="ReturnType" type="String" optional hint="exact,partial,start,end,count" />
376
377 <cfif StructKeyExists(Arguments,'ReturnType')>
378 <cfset Arguments.ReturnType = LCase(Arguments.ReturnType) />
379
380 <!--- INFO: If no unnamed args, don't waste time checking for them. --->
381 <cfelseif StructCount(arguments) EQ 2>
382 <cfset Arguments.ReturnType = 'exact' />
383
384 <cfelse>
385 <cfif StructKeyExists(Arguments,'Exact') AND Arguments.Exact >
386 <cfset Arguments.ReturnType = "exact" />
387 <cfelseif StructKeyExists(Arguments,'Count') AND Arguments.Count >
388 <cfset Arguments.ReturnType = "count" />
389 <cfelse>
390 <cfif StructKeyExists(Arguments,'at')>
391 <cfif Arguments.At EQ 'anywhere'>
392 <cfset Arguments.ReturnType = 'partial' />
393 <cfelse>
394 <cfset Arguments.ReturnType = LCase(Arguments.At) />
395 </cfif>
396 <cfelseif StructKeyExists(Arguments,'Partial') AND Arguments.Partial >
397 <cfset Arguments.ReturnType = "partial" />
398 </cfif>
399 </cfif>
400 <cfif NOT StructKeyExists(Arguments,'ReturnType')>
401 <cfset Arguments.ReturnType = 'exact' />
402 </cfif>
403 </cfif>
404
405 <cfswitch expression="#Arguments.ReturnType#">
406 <cfcase value="exact">
407 <cfreturn Variables.PatternObject.Matcher(Arguments.Text).matches() />
408 </cfcase>
409 <cfcase value="count">
410 <cfset var Matcher = Variables.PatternObject.Matcher(Arguments.Text) />
411 <cfset local.Count = 0 />
412 <cfloop condition="Matcher.find()">
413 <cfset local.Count++ />
414 </cfloop>
415 <cfreturn local.Count />
416 </cfcase>
417 <cfcase value="start">
418 <cfreturn Variables.PatternObject.Matcher(Arguments.Text).lookingAt() />
419 </cfcase>
420 <cfcase value="end">
421 <cfset var Matcher = Variables.PatternObject.Matcher(Arguments.Text) />
422 <cfset var LastPos = -1 />
423 <cfloop condition="Matcher.find()">
424 <cfset LastPos = Matcher.end() />
425 </cfloop>
426 <cfreturn (LastPos EQ Len(Arguments.Text)) />
427 </cfcase>
428 <cfcase value="partial">
429 <cfreturn Variables.PatternObject.Matcher(Arguments.Text).find() />
430 </cfcase>
431 <cfdefaultcase>
432 <cfthrow
433 message = "Invalid ReturnType '#Arguments.ReturnType#' for matches"
434 type = "cfRegex.Match.InvalidArgument.ReturnType"
435 />
436 </cfdefaultcase>
437 </cfswitch>
438 </cffunction>
439
440
441 <cffunction name="escape" returntype="String" output="false" access="public" action>
442 <cfargument name="ReturnType" type="String" default=REGEX hint="regex|class" />
443 <cfif NOT ListFind('regex,class',LCase(Arguments.ReturnType))>
444 <cfthrow
445 message = "Invalid Argument ReturnType, received [#Arguments.ReturnType#]"
446 detail = "ReturnType value must be one of 'regex' OR 'class'."
447 type = "cfRegex.Escape.InvalidArgument.ReturnType"
448 />
449 </cfif>
450 <cfif NOT StructKeyExists(Variables,'Escaped#Arguments.ReturnType#')>
451 <cfif Arguments.ReturnType EQ 'regex'>
452 <cfset Variables.EscapedRegex = Variables.PatternText.replaceAll('[$^*()+\[\]{}.?\\|]','\\$0') />
453 <cfelse>
454 <cfset Variables.EscapedClass = Variables.PatternText
455 .replaceAll('(.)(?=.*?\1)','')
456 .replaceAll('(^\^|[\\\-\[\]])','\\$0')
457 .replaceAll(chr(9),'\t')
458 .replaceAll(chr(10),'\n')
459 .replaceAll(chr(13),'\r')
460 />
461 </cfif>
462 <cfif BitAnd(Variables.ActiveModes,Variables.Modes['COMMENTS']) >
463 <cfset Variables['Escaped#Arguments.ReturnType#'] = Variables['Escaped#Arguments.ReturnType#'].replaceAll('##| ','\\$0') />
464 </cfif>
465 </cfif>
466 <cfreturn Variables['Escaped#Arguments.ReturnType#'] />
467 </cffunction>
468
469
470 <cffunction name="quote" returntype="String" output="false" access="public" action>
471 <cfif NOT StructKeyExists(Variables,'Quoted')>
472 <cfset Variables.Quoted = createObject("java","java.util.regex.Pattern").quote(Variables.PatternText) />
473 </cfif>
474 <cfreturn Variables.Quoted />
475 </cffunction>
476
477
478 <cffunction name="replace" returntype="String" output="false" access="public" action>
479 <cfargument name="Text" type="String" required="true" />
480 <cfargument name="Replacement" type="Any" optional hint="String,Array,Function"/>
481 <cfargument name="Start" type="Numeric" optional />
482 <cfargument name="Limit" type="Numeric" default=0 />
483 <cfargument name="GroupNames" type="any" default="" hint="Passed into Callback function if provided" />
484 <cfargument name="CallbackData" type="Struct" optional hint="Extra data which is passed in to callback function." />
485
486 <cfif StructKeyExists(Arguments,'Callback') >
487 <cfset Arguments.Replacement = Arguments.Callback />
488 <cfelseif NOT StructKeyExists(Arguments,'Replacement')>
489 <cfthrow
490 message = "Missing Argument Replacement"
491 type = "cfRegex.Replace.MissingArgument"
492 />
493 </cfif>
494
495 <cfset var Prefix = "" />
496 <cfset var Offset = 1 />
497 <cfif StructKeyExists(Arguments,'Start') AND Arguments.Start >
498 <cfset Offset = Arguments.Start+1 />
499 <cfset Prefix = Left(Arguments.Text,Arguments.Start) />
500 <cfset Arguments.Text = Mid(Arguments.Text,Arguments.Start+1,Len(Arguments.Text)) />
501 </cfif>
502
503 <cfset var Matcher = Variables.PatternObject.Matcher( Arguments.Text )/>
504 <cfset var Results = createObject("java","java.lang.StringBuffer").init(Prefix)/>
505 <cfset var ReplacementsMade = 0 />
506 <cfset var ReplacePos = 1 />
507
508 <cfif NOT StructKeyExists(Arguments,'CallbackData')>
509 <cfset Arguments.CallbackData = {} />
510 </cfif>
511
512 <cfloop condition="Matcher.find()">
513
514 <cfif isSimpleValue(Arguments.Replacement)>
515 <cfset Matcher.appendReplacement( Results , Arguments.Replacement )/>
516
517 <cfelseif isArray(Arguments.Replacement)>
518
519 <cfif isSimpleValue(Arguments.Replacement[ReplacePos])>
520 <cfset Matcher.appendReplacement( Results , Arguments.Replacement[ReplacePos] )/>
521 <cfelse>
522 <cfset var CurrentReplaceFunc = Arguments.Replacement[ReplacePos] />
523<cfset Matcher.appendReplacement
524 ( Results
525 , CurrentReplaceFunc( ArgumentCollection=buildMatchInfo(Matcher,Offset,Arguments.GroupNames) , Data = Arguments.CallbackData )
526 )/>
527 </cfif>
528
529 <cfif ++ReplacePos GT ArrayLen(Arguments.Replacement)>
530 <cfset ReplacePos = 1 />
531 </cfif>
532
533 <cfelse>
534
535 <cfset Matcher.appendReplacement
536 ( Results
537 , Arguments.Replacement( ArgumentCollection=buildMatchInfo(Matcher,Offset,Arguments.GroupNames) , Data = Arguments.CallbackData )
538 )/>
539
540 </cfif>
541
542 <cfif ++ReplacementsMade EQ Arguments.Limit>
543 <cfbreak/>
544 </cfif>
545
546 </cfloop>
547
548 <cfset Matcher.appendTail(Results)/>
549
550 <cfreturn Results.toString() />
551 </cffunction>
552
553
554 <cffunction name="split" returntype="Array" output="false" access="public" action>
555 <cfargument name="Text" type="String" required="true" />
556 <cfargument name="Start" type="Numeric" optional />
557 <cfargument name="Limit" type="Numeric" default=0 hint="The maximum number of times a split is made (i.e. limit+1=max array size)"/>
558 <cfargument name="GroupNames" type="any" default="" hint="Passed into Callback function if provided" />
559 <cfargument name="Callback" type="any" optional />
560 <cfargument name="CallbackData" type="Struct" optional hint="Extra data which is passed in to callback function." />
561
562 <cfset var Offset = 1 />
563 <cfif StructKeyExists(Arguments,'Start') AND Arguments.Start >
564 <cfset var Prefix = Left(Arguments.Text,Arguments.Start) />
565 <cfset Offset = 1+Arguments.Start />
566 <cfset Arguments.Text = Mid(Arguments.Text,Arguments.Start+1,Len(Arguments.Text)) />
567 </cfif>
568
569 <cfif StructKeyExists(Arguments,'Callback')>
570 <cfset var Matcher = Variables.PatternObject.Matcher( Arguments.Text )/>
571 <cfset var TextPos = 1 />
572 <cfset var ArrayPos = 1 />
573 <cfset var Results = [''] />
574 <cfif NOT StructKeyExists(Arguments,'CallbackData')>
575 <cfset Arguments.CallbackData = {} />
576 </cfif>
577
578 <cfloop condition="Matcher.find(TextPos-1)">
579
580 <cfif Arguments.Callback( ArgumentCollection=buildMatchInfo(Matcher,Offset,Arguments.GroupNames) , Data=Arguments.CallbackData )>
581
582 <cfset Results[ArrayPos] &= mid(Arguments.Text,TextPos,Matcher.start()+1-TextPos) />
583 <cfset TextPos = Matcher.end()+1 />
584
585 <cfset ArrayPos++ />
586 <cfset Results[ArrayPos] = '' />
587
588 <cfif Arguments.Limit AND ArrayLen(Results) GT Arguments.Limit>
589 <cfbreak />
590 </cfif>
591 <cfelse>
592 <cfset Results[ArrayPos] &= mid(Arguments.Text,TextPos,Matcher.end()+1-TextPos) />
593 <cfset TextPos = Matcher.end()+1 />
594 </cfif>
595
596 </cfloop>
597
598 <cfset Results[ArrayPos] &= mid(Arguments.Text,TextPos,len(Arguments.Text)) />
599
600 <cfelse>
601 <cfif Arguments.Limit>
602 <!---
603 NOTE:
604 For java.util.regex, limit is array length.
605 For cfregex, limit is number of times the action occurs.
606 Therefor, must add one...
607 --->
608 <cfset var Results = Variables.PatternObject.split(Arguments.Text,Arguments.Limit+1) />
609 <cfelse>
610 <cfset var Results = Variables.PatternObject.split(Arguments.Text) />
611 </cfif>
612 </cfif>
613
614 <cfif isDefined('Prefix') AND ArrayLen(Results)>
615 <cfset Results[1] = Prefix & Results[1] />
616 </cfif>
617
618 <cfreturn Results />
619 </cffunction>
620
621 <!---
622 /// EXTERNAL ///
623 --->
624
625
626
627 <!---
628 CALLBACK SAMPLES
629
630 A callback function can be used with the following functions:
631 .replace
632 .match
633 .split
634
635 A callback is called each time a match is found, and allows for
636 conditional behaviour to be executed at this point,
637 to change how the function behaves towards the match.
638
639 A Replace Callback determines what text to use for replacement.
640 A Match Callback determines whether to include or exclude the match in results.
641 A Split Callback determines whether to split or not at the match.
642
643 The callbacks are identical except for returntype.
644 (For Replace it returns text, for everything else, it returns a boolean.)
645
646 See http://docs.cfregex.net/Callbacks.html
647
648 <cffunction name="ReplaceCallback" returntype="String" output="false">
649 <cfargument name="Pos" type="Numeric" required="true" hint="The start position of the match." />
650 <cfargument name="Len" type="Numeric" required="true" hint="The length of the match." />
651 <cfargument name="Match" type="String" required="true" hint="The text of the match." />
652 <cfargument name="Groups" type="Array" required="true" hint="Array of group information." />
653 <cfargument name="NamedGroups" type="Struct" optional hint="Struct of named group information." />
654 <cfargument name="Data" type="Struct" optional hint="Struct containing passed-in data." />
655
656 <cfreturn 'replacement text' />
657 </cffunction>
658
659
660 <cffunction name="BooleanCallback" returntype="Boolean" output="false">
661 <cfargument name="Pos" type="Numeric" required="true" hint="The start position of the match." />
662 <cfargument name="Len" type="Numeric" required="true" hint="The length of the match." />
663 <cfargument name="Match" type="String" required="true" hint="The text of the match." />
664 <cfargument name="Groups" type="Array" required="true" hint="Array of group information." />
665 <cfargument name="NamedGroups" type="Struct" optional hint="Struct of named group information." />
666 <cfargument name="Data" type="Struct" optional hint="Struct containing passed-in data." />
667
668 <cfreturn true />
669 </cffunction>
670
671 --->
672
673
674</cfcomponent>