28
28
import net .sf .saxon .functions .Replace ;
29
29
import net .sf .saxon .regex .RegularExpression ;
30
30
import org .exist .dom .QName ;
31
- import org .exist .xquery .Atomize ;
32
- import org .exist .xquery .Cardinality ;
33
- import org .exist .xquery .Dependency ;
34
- import org .exist .xquery .DynamicCardinalityCheck ;
35
- import org .exist .xquery .ErrorCodes ;
36
- import org .exist .xquery .Expression ;
37
- import org .exist .xquery .Function ;
38
- import org .exist .xquery .FunctionSignature ;
39
- import org .exist .xquery .Profiler ;
40
- import org .exist .xquery .XPathException ;
41
- import org .exist .xquery .XQueryContext ;
42
- import org .exist .xquery .util .Error ;
31
+ import org .exist .xquery .*;
43
32
import org .exist .xquery .value .FunctionParameterSequenceType ;
44
- import org .exist .xquery .value .FunctionReturnSequenceType ;
45
- import org .exist .xquery .value .Item ;
46
33
import org .exist .xquery .value .Sequence ;
47
- import org .exist .xquery .value .SequenceType ;
48
34
import org .exist .xquery .value .StringValue ;
49
35
import org .exist .xquery .value .Type ;
50
36
37
+ import static org .exist .xquery .FunctionDSL .*;
51
38
import static org .exist .xquery .regex .RegexUtil .*;
52
39
53
40
/**
54
41
* @author <a href="mailto:[email protected] ">Adam Retter</a>
55
42
* @author <a href="mailto:[email protected] ">Wolfgang Meier</a>
56
43
*/
57
- public class FunReplace extends FunMatches {
58
-
59
- private static final String FUNCTION_DESCRIPTION_3_PARAM =
60
- "The function returns the xs:string that is obtained by replacing each non-overlapping substring " +
61
- "of $input that matches the given $pattern with an occurrence of the $replacement string.\n \n " ;
62
- private static final String FUNCTION_DESCRIPTION_4_PARAM =
44
+ public class FunReplace extends BasicFunction {
45
+
46
+ private static final QName FS_REPLACE_NAME = new QName ("replace" , Function .BUILTIN_FUNCTION_NS );
47
+
48
+ private static final String FS_REPLACE_DESCRIPTION =
63
49
"The function returns the xs:string that is obtained by replacing each non-overlapping substring " +
64
50
"of $input that matches the given $pattern with an occurrence of the $replacement string.\n \n " +
65
51
"The $flags argument is interpreted in the same manner as for the fn:matches() function.\n \n " +
66
52
"Calling the four argument version with the $flags argument set to a " +
67
- "zero-length string gives the same effect as using the three argument version.\n \n " ;
68
- private static final String FUNCTION_DESCRIPTION_COMMON =
53
+ "zero-length string gives the same effect as using the three argument version.\n \n " +
69
54
"If $input is the empty sequence, it is interpreted as the zero-length string.\n \n If two overlapping " +
70
55
"substrings of $input both match the $pattern, then only the first one (that is, the one whose first " +
71
56
"character comes first in the $input string) is replaced.\n \n Within the $replacement string, a variable " +
@@ -85,103 +70,59 @@ public class FunReplace extends FunMatches {
85
70
"included \" as is\" in the replacement string, and the rules are reapplied using the number N " +
86
71
"formed by stripping off this last digit." ;
87
72
88
- protected static final FunctionParameterSequenceType INPUT_ARG = new FunctionParameterSequenceType ("input" , Type .STRING , Cardinality .ZERO_OR_ONE , "The input string" );
89
- protected static final FunctionParameterSequenceType PATTERN_ARG = new FunctionParameterSequenceType ("pattern" , Type .STRING , Cardinality .EXACTLY_ONE , "The pattern to match" );
90
- protected static final FunctionParameterSequenceType REPLACEMENT_ARG = new FunctionParameterSequenceType ("replacement" , Type .STRING , Cardinality .EXACTLY_ONE , "The string to replace the pattern with" );
91
- protected static final FunctionParameterSequenceType FLAGS_ARG = new FunctionParameterSequenceType ("flags" , Type .STRING , Cardinality .EXACTLY_ONE , "The flags" );
92
- protected static final FunctionReturnSequenceType RETURN_TYPE = new FunctionReturnSequenceType (Type .STRING , Cardinality .EXACTLY_ONE , "the altered string" );
93
-
94
- public final static FunctionSignature [] signatures = {
95
- new FunctionSignature (
96
- new QName ("replace" , Function .BUILTIN_FUNCTION_NS ),
97
- FUNCTION_DESCRIPTION_3_PARAM + FUNCTION_DESCRIPTION_COMMON ,
98
- new SequenceType [] { INPUT_ARG , PATTERN_ARG , REPLACEMENT_ARG },
99
- RETURN_TYPE
100
- ),
101
- new FunctionSignature (
102
- new QName ("replace" , Function .BUILTIN_FUNCTION_NS ),
103
- FUNCTION_DESCRIPTION_4_PARAM + FUNCTION_DESCRIPTION_COMMON ,
104
- new SequenceType [] { INPUT_ARG , PATTERN_ARG , REPLACEMENT_ARG , FLAGS_ARG },
105
- RETURN_TYPE
106
- )
107
- };
73
+ private static final FunctionParameterSequenceType FS_TOKENIZE_PARAM_INPUT = optParam ("input" , Type .STRING , "The input string" );
74
+ private static final FunctionParameterSequenceType FS_TOKENIZE_PARAM_PATTERN = param ("pattern" , Type .STRING , "The pattern to match" );
75
+ private static final FunctionParameterSequenceType FS_TOKENIZE_PARAM_REPLACEMENT = param ("replacement" , Type .STRING , "The string to replace the pattern with" );
76
+
77
+ static final FunctionSignature [] FS_REPLACE = functionSignatures (
78
+ FS_REPLACE_NAME ,
79
+ FS_REPLACE_DESCRIPTION ,
80
+ returns (Type .STRING , "the altered string" ),
81
+ arities (
82
+ arity (
83
+ FS_TOKENIZE_PARAM_INPUT ,
84
+ FS_TOKENIZE_PARAM_PATTERN ,
85
+ FS_TOKENIZE_PARAM_REPLACEMENT
86
+ ),
87
+ arity (
88
+ FS_TOKENIZE_PARAM_INPUT ,
89
+ FS_TOKENIZE_PARAM_PATTERN ,
90
+ FS_TOKENIZE_PARAM_REPLACEMENT ,
91
+ param ("flags" , Type .STRING , Cardinality .EXACTLY_ONE , "The flags" )
92
+ )
93
+ )
94
+ );
108
95
109
96
public FunReplace (final XQueryContext context , final FunctionSignature signature ) {
110
97
super (context , signature );
111
98
}
112
-
113
- @ Override
114
- public void setArguments (List <Expression > arguments ) {
115
- steps .clear ();
116
- Expression arg = arguments .get (0 );
117
- arg = new DynamicCardinalityCheck (context , Cardinality .ZERO_OR_ONE , arg ,
118
- new Error (Error .FUNC_PARAM_CARDINALITY , "1" , getSignature ()));
119
- if (!Type .subTypeOf (arg .returnsType (), Type .ATOMIC ))
120
- {arg = new Atomize (context , arg );}
121
- steps .add (arg );
122
-
123
- arg = arguments .get (1 );
124
- arg = new DynamicCardinalityCheck (context , Cardinality .EXACTLY_ONE , arg ,
125
- new Error (Error .FUNC_PARAM_CARDINALITY , "2" , getSignature ()));
126
- if (!Type .subTypeOf (arg .returnsType (), Type .ATOMIC ))
127
- {arg = new Atomize (context , arg );}
128
- steps .add (arg );
129
-
130
- arg = arguments .get (2 );
131
- arg = new DynamicCardinalityCheck (context , Cardinality .EXACTLY_ONE , arg ,
132
- new Error (Error .FUNC_PARAM_CARDINALITY , "3" , getSignature ()));
133
- if (!Type .subTypeOf (arg .returnsType (), Type .ATOMIC ))
134
- {arg = new Atomize (context , arg );}
135
- steps .add (arg );
136
-
137
- if (arguments .size () == 4 ) {
138
- arg = arguments .get (3 );
139
- arg = new DynamicCardinalityCheck (context , Cardinality .EXACTLY_ONE , arg ,
140
- new Error (Error .FUNC_PARAM_CARDINALITY , "4" , getSignature ()));
141
- if (!Type .subTypeOf (arg .returnsType (), Type .ATOMIC ))
142
- {arg = new Atomize (context , arg );}
143
- steps .add (arg );
144
- }
145
- }
146
99
147
100
@ Override
148
- public Sequence eval (final Sequence contextSequence , final Item contextItem ) throws XPathException {
149
- if (context .getProfiler ().isEnabled ()) {
150
- context .getProfiler ().start (this );
151
- context .getProfiler ().message (this , Profiler .DEPENDENCIES , "DEPENDENCIES" , Dependency .getDependenciesName (this .getDependencies ()));
152
- if (contextSequence != null ) {
153
- context .getProfiler ().message (this , Profiler .START_SEQUENCES , "CONTEXT SEQUENCE" , contextSequence );
154
- }
155
- if (contextItem != null ) {
156
- context .getProfiler ().message (this , Profiler .START_SEQUENCES , "CONTEXT ITEM" , contextItem .toSequence ());
157
- }
158
- }
159
-
101
+ public Sequence eval (final Sequence [] args , final Sequence contextSequence ) throws XPathException {
160
102
final Sequence result ;
161
- final Sequence stringArg = getArgument ( 0 ). eval ( contextSequence , contextItem ) ;
103
+ final Sequence stringArg = args [ 0 ] ;
162
104
if (stringArg .isEmpty ()) {
163
105
result = StringValue .EMPTY_STRING ;
164
106
} else {
165
107
final String flags ;
166
- if (getSignature (). getArgumentCount () == 4 ) {
167
- flags = getArgument ( 3 ). eval ( contextSequence , contextItem ).getStringValue ();
108
+ if (args . length == 4 ) {
109
+ flags = args [ 3 ]. itemAt ( 0 ).getStringValue ();
168
110
} else {
169
111
flags = "" ;
170
112
}
171
-
172
113
final String string = stringArg .getStringValue ();
173
- final Sequence patternSeq = getArgument (1 ).eval (contextSequence , contextItem );
174
- final String pattern = patternSeq .getStringValue ();
175
-
176
- final Sequence replaceSeq = getArgument (2 ).eval (contextSequence , contextItem );
177
- final String replace = replaceSeq .getStringValue ();
114
+ final String pattern = args [1 ].itemAt (0 ).getStringValue ();
115
+ final String replace = args [2 ].itemAt (0 ).getStringValue ();
178
116
179
117
final Configuration config = context .getBroker ().getBrokerPool ().getSaxonConfiguration ();
180
118
181
119
final List <String > warnings = new ArrayList <>(1 );
182
120
183
121
try {
184
122
final RegularExpression regularExpression = config .compileRegularExpression (pattern , flags , "XP30" , warnings );
123
+ if (regularExpression .matches ("" )) {
124
+ throw new XPathException (this , ErrorCodes .FORX0003 , "regular expression could match empty string" );
125
+ }
185
126
186
127
//TODO(AR) cache the regular expression... might be possible through Saxon config
187
128
@@ -210,11 +151,6 @@ public Sequence eval(final Sequence contextSequence, final Item contextItem) thr
210
151
}
211
152
}
212
153
213
- if (context .getProfiler ().isEnabled ()) {
214
- context .getProfiler ().end (this , "" , result );
215
- }
216
-
217
- return result ;
218
-
154
+ return result ;
219
155
}
220
156
}
0 commit comments