Skip to content

Commit 9cb79d1

Browse files
Added new escaper for already percent encoded inputs
1 parent 724e74f commit 9cb79d1

File tree

2 files changed

+89
-0
lines changed

2 files changed

+89
-0
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.google.api.client.util.escape;
2+
3+
import java.util.regex.Matcher;
4+
import java.util.regex.Pattern;
5+
6+
/**
7+
* An {@link Escaper} implementation that preserves percent-encoded sequences in the input string.
8+
*
9+
* <p>This escaper applies the provided {@link Escaper} to all parts of the input string except for
10+
* valid percent-encoded sequences (e.g., <code>%20</code>), which are left unchanged.
11+
*/
12+
public class PercentEncodedEscaper extends Escaper {
13+
14+
/** Pattern to match valid percent-encoded sequences (e.g., %20). */
15+
static final Pattern PCT_ENCODE_PATTERN = Pattern.compile("%[0-9A-Fa-f]{2}");
16+
17+
private final Escaper escaper;
18+
19+
public PercentEncodedEscaper(Escaper escaper) {
20+
if (escaper == null) {
21+
throw new NullPointerException("Escaper cannot be null");
22+
}
23+
this.escaper = escaper;
24+
}
25+
26+
/**
27+
* Escapes the input string using the provided {@link Escaper}, preserving valid percent-encoded
28+
* sequences.
29+
*
30+
* @param string the input string to escape
31+
* @return the escaped string with percent-encoded sequences left unchanged
32+
*/
33+
@Override
34+
public String escape(String string) {
35+
if (string == null || string.isEmpty()) {
36+
return string;
37+
}
38+
39+
Matcher matcher = PCT_ENCODE_PATTERN.matcher(string);
40+
StringBuilder sb = new StringBuilder();
41+
42+
int lastEnd = 0;
43+
while (matcher.find()) {
44+
sb.append(escaper.escape(string.substring(lastEnd, matcher.start())));
45+
46+
sb.append(string.substring(matcher.start(), matcher.end()));
47+
48+
lastEnd = matcher.end();
49+
}
50+
51+
if (lastEnd < string.length()) {
52+
sb.append(escaper.escape(string.substring(lastEnd)));
53+
}
54+
55+
return sb.toString();
56+
}
57+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.google.api.client.util.escape;
2+
3+
import junit.framework.TestCase;
4+
import org.junit.Test;
5+
import org.junit.runner.RunWith;
6+
import org.junit.runners.JUnit4;
7+
8+
@RunWith(JUnit4.class)
9+
public class PercentEncodedEscaperTest extends TestCase {
10+
@Test
11+
public void testEscape() {
12+
PercentEncodedEscaper escaper =
13+
new PercentEncodedEscaper(
14+
new PercentEscaper(PercentEscaper.SAFE_PLUS_RESERVED_CHARS_URLENCODER));
15+
String input = "Hello%20World+/?#[]";
16+
17+
String actual = escaper.escape(input);
18+
assertEquals(input, actual); // No change expected since it's already percent-encoded
19+
}
20+
21+
@Test
22+
public void testEscapeEncode() {
23+
PercentEncodedEscaper escaper =
24+
new PercentEncodedEscaper(
25+
new PercentEscaper(PercentEscaper.SAFE_PLUS_RESERVED_CHARS_URLENCODER));
26+
String input = "Hello World%";
27+
String expected = "Hello%20World%25";
28+
29+
String actual = escaper.escape(input);
30+
assertEquals(expected, actual);
31+
}
32+
}

0 commit comments

Comments
 (0)