Skip to content

Commit 94b3192

Browse files
agnikagnik
authored andcommitted
Implement appendTail and appendReplacement methods in Matcher (#589)
1 parent c450e0b commit 94b3192

3 files changed

Lines changed: 190 additions & 3 deletions

File tree

src/classes/modules/java.base/java/util/regex/Matcher.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,22 @@ public int groupCount() {
160160
}
161161

162162

163-
// TODO public native MatchResult toMatchResult();-Done;
164-
// TODO public native StringBuffer appendTail(StringBuffer sb);
165-
// TODO public native Matcher appendReplacement(StringBuffer sb, String replacement);
163+
/**
164+
* Appends the input subsequence after the last match to the given StringBuffer.
165+
*
166+
* @param sb The target string buffer
167+
* @return The target string buffer
168+
*/
169+
public native StringBuffer appendTail(StringBuffer sb);
170+
171+
/**
172+
* Implements a non-terminal append-and-replace step.
173+
* Reads characters from the input sequence, appends them to the given
174+
* string buffer, and then appends the replacement string.
175+
*
176+
* @param sb The target string buffer
177+
* @param replacement The replacement string
178+
* @return This matcher
179+
*/
180+
public native Matcher appendReplacement(StringBuffer sb, String replacement);
166181
}

src/peers/gov/nasa/jpf/vm/JPF_java_util_regex_Matcher.java

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,4 +244,76 @@ public boolean requireEnd____Z (MJIEnv env, int objref) {
244244
Matcher matcher = getInstance( env, objref);
245245
return matcher.requireEnd();
246246
}
247+
248+
@MJI
249+
public int appendTail__Ljava_lang_StringBuffer_2__Ljava_lang_StringBuffer_2(MJIEnv env, int objref, int sbRef) {
250+
Matcher matcher = getInstance(env, objref);
251+
252+
// Use a host-side StringBuffer to capture the tail content
253+
StringBuffer hostSb = new StringBuffer();
254+
matcher.appendTail(hostSb);
255+
String tail = hostSb.toString();
256+
257+
if (tail.length() > 0) {
258+
// Append the tail to the JPF StringBuffer via direct call
259+
ThreadInfo ti = env.getThreadInfo();
260+
DirectCallStackFrame frame = ti.getReturnedDirectCall();
261+
262+
if (frame == null) {
263+
ClassInfo sbClass = env.getClassInfo(sbRef);
264+
MethodInfo appendMi = sbClass.getMethod("append(Ljava/lang/String;)Ljava/lang/StringBuffer;", false);
265+
266+
if (appendMi != null) {
267+
int tailRef = env.newString(tail);
268+
frame = appendMi.createDirectCallStackFrame(ti, 1);
269+
int argOffset = frame.setReferenceArgument(0, sbRef, null);
270+
frame.setReferenceArgument(argOffset, tailRef, null);
271+
ti.pushFrame(frame);
272+
env.repeatInvocation();
273+
return MJIEnv.NULL;
274+
}
275+
}
276+
}
277+
278+
return sbRef;
279+
}
280+
281+
@MJI
282+
public int appendReplacement__Ljava_lang_StringBuffer_2Ljava_lang_String_2__Ljava_util_regex_Matcher_2(MJIEnv env, int objref, int sbRef, int replacementRef) {
283+
Matcher matcher = getInstance(env, objref);
284+
285+
ThreadInfo ti = env.getThreadInfo();
286+
DirectCallStackFrame frame = ti.getReturnedDirectCall();
287+
288+
if (frame == null) {
289+
try {
290+
String replacement = env.getStringObject(replacementRef);
291+
292+
// Use a host-side StringBuffer to capture the appendReplacement result
293+
StringBuffer hostSb = new StringBuffer();
294+
matcher.appendReplacement(hostSb, replacement);
295+
String content = hostSb.toString();
296+
297+
if (content.length() > 0) {
298+
ClassInfo sbClass = env.getClassInfo(sbRef);
299+
MethodInfo appendMi = sbClass.getMethod("append(Ljava/lang/String;)Ljava/lang/StringBuffer;", false);
300+
301+
if (appendMi != null) {
302+
int contentRef = env.newString(content);
303+
frame = appendMi.createDirectCallStackFrame(ti, 1);
304+
int argOffset = frame.setReferenceArgument(0, sbRef, null);
305+
frame.setReferenceArgument(argOffset, contentRef, null);
306+
ti.pushFrame(frame);
307+
env.repeatInvocation();
308+
return MJIEnv.NULL;
309+
}
310+
}
311+
} catch (IllegalStateException e) {
312+
env.throwException("java.lang.IllegalStateException", "No match available");
313+
return MJIEnv.NULL;
314+
}
315+
}
316+
317+
return objref;
318+
}
247319
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright (C) 2014, United States Government, as represented by the
3+
* Administrator of the National Aeronautics and Space Administration.
4+
* All rights reserved.
5+
*
6+
* The Java Pathfinder core (jpf-core) platform is licensed under the
7+
* Apache License, Version 2.0 (the "License"); you may not use this file except
8+
* in compliance with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0.
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package gov.nasa.jpf.test.java.util.regex;
20+
21+
import gov.nasa.jpf.util.test.TestJPF;
22+
import org.junit.Test;
23+
24+
import java.util.regex.Matcher;
25+
import java.util.regex.Pattern;
26+
27+
/**
28+
* Tests for Matcher.appendTail() and Matcher.appendReplacement() methods.
29+
*/
30+
public class MatcherAppendTest extends TestJPF {
31+
32+
@Test
33+
public void testAppendReplacementAndTail() {
34+
if (verifyNoPropertyViolation()) {
35+
Pattern p = Pattern.compile("cat");
36+
Matcher m = p.matcher("one cat two cats in the cat house");
37+
StringBuffer sb = new StringBuffer();
38+
while (m.find()) {
39+
m.appendReplacement(sb, "dog");
40+
}
41+
m.appendTail(sb);
42+
String result = sb.toString();
43+
assertEquals("one dog two dogs in the dog house", result);
44+
}
45+
}
46+
47+
@Test
48+
public void testAppendReplacementSingleMatch() {
49+
if (verifyNoPropertyViolation()) {
50+
Pattern p = Pattern.compile("hello");
51+
Matcher m = p.matcher("say hello world");
52+
StringBuffer sb = new StringBuffer();
53+
if (m.find()) {
54+
m.appendReplacement(sb, "hi");
55+
}
56+
m.appendTail(sb);
57+
assertEquals("say hi world", sb.toString());
58+
}
59+
}
60+
61+
@Test
62+
public void testAppendTailNoMatch() {
63+
if (verifyNoPropertyViolation()) {
64+
Pattern p = Pattern.compile("xyz");
65+
Matcher m = p.matcher("no match here");
66+
StringBuffer sb = new StringBuffer();
67+
// No find() called - just appendTail
68+
m.appendTail(sb);
69+
assertEquals("no match here", sb.toString());
70+
}
71+
}
72+
73+
@Test
74+
public void testAppendReplacementWithGroupReference() {
75+
if (verifyNoPropertyViolation()) {
76+
Pattern p = Pattern.compile("(\\d+)");
77+
Matcher m = p.matcher("item1 and item2");
78+
StringBuffer sb = new StringBuffer();
79+
while (m.find()) {
80+
m.appendReplacement(sb, "[$1]");
81+
}
82+
m.appendTail(sb);
83+
assertEquals("item[1] and item[2]", sb.toString());
84+
}
85+
}
86+
87+
@Test
88+
public void testAppendReplacementEmptyString() {
89+
if (verifyNoPropertyViolation()) {
90+
Pattern p = Pattern.compile("remove");
91+
Matcher m = p.matcher("please remove this");
92+
StringBuffer sb = new StringBuffer();
93+
while (m.find()) {
94+
m.appendReplacement(sb, "");
95+
}
96+
m.appendTail(sb);
97+
assertEquals("please this", sb.toString());
98+
}
99+
}
100+
}

0 commit comments

Comments
 (0)