Skip to content

Commit

Permalink
Adds escaping of single quotes and support for .net style escaping
Browse files Browse the repository at this point in the history
DEVSIX-6308
  • Loading branch information
glenn.volckaert authored and Ubuntu committed Apr 5, 2022
1 parent ae8cf77 commit d5b0e93
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,87 @@ private MessageFormatUtil() {
// Empty constructor.
}

/**
* This method provides a generic way for formatting strings.
* Indexed arguments can be referred with {index},
* to escape curly braces you have to double them.
*
* @param pattern to format
* @param arguments arguments
*
* @return The formatted string
*/
public static String format(String pattern, Object... arguments) {
return new MessageFormat(pattern, Locale.ROOT).format(arguments);
boolean mustClose = false;
StringBuilder result = new StringBuilder(pattern.length());
int i = 0;
int n = pattern.length();
while (i < n) {
char current = pattern.charAt(i);
switch (current) {
case '{': {
int curlyCount = 0;
int j;
for (j = i; j < n && pattern.charAt(j) == '{'; j++, curlyCount++)
;
i += curlyCount - 1;
if (curlyCount > 1) {
if (!mustClose) {
result.append("'");
}
while (curlyCount >= 2) {
result.append('{');
curlyCount -= 2;
}
mustClose = true;
}
if (curlyCount == 1) {
if (mustClose) {
result.append('\'');
}
result.append('{');
mustClose = false;
}
}
break;
case '}': {
int curlyCount = 0;
int j;
for (j = i; j < n && pattern.charAt(j) == '}'; j++, curlyCount++)
;
i += curlyCount - 1;
if (curlyCount % 2 == 1) {
if (mustClose) {
result.append('\'');
}
result.append('}');
mustClose = false;
}
if (curlyCount > 1) {
result.append("'");
while (curlyCount >= 2) {
result.append('}');
curlyCount -= 2;
}
mustClose = true;
}
}
break;
case '\'':
result.append("''");
break;
default:
if (mustClose) {
result.append('\'');
mustClose = false;
}
result.append(current);
}
i++;
}
if (mustClose) {
result.append('\'');
}
return new MessageFormat(result.toString(), Locale.ROOT).format(arguments);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
This file is part of the iText (R) project.
Copyright (c) 1998-2022 iText Group NV
Authors: iText Software.
This program is offered under a commercial and under the AGPL license.
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
AGPL licensing:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itextpdf.commons.utils;

import com.itextpdf.test.ExtendedITextTest;

import java.awt.SystemTray;
import java.io.Console;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Stack;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

@RunWith(Parameterized.class)
public class MessageFormatUtilTest extends ExtendedITextTest {

private String expectedResult;
private String pattern;
private Object[] arguments;


public MessageFormatUtilTest(Object expectedResult, Object pattern, Object arguments, Object name) {
this.expectedResult = (String)expectedResult;
this.pattern = (String)pattern;
this.arguments = (Object[]) arguments;
}


@Parameterized.Parameters(name = "{index}: {3} format: {1}; {0}")
public static Iterable<Object[]> dataSource() {
return Arrays.asList(new Object[][]{
{"Plain message with params 1 test", "Plain message with params {0} {1}", new Object[] {1, "test"},"test with simple params"},
{"Message with 'single quotes'", "Message with 'single quotes'", new Object[0],"test with single quotes"},
{"Message with ''doubled single quotes''", "Message with ''doubled single quotes''", new Object[0],"test with doubled single quotes"},
{"Message with {curly braces} and a parameter {I'm between curly braces too}", "Message with {{curly braces}} and a parameter {{{0}}}", new Object[]{"I'm between curly braces too"},"Test with curly braces"},
{"Message with {{multiple curly braces}}", "Message with {{{{multiple curly braces}}}}", new Object[]{},"Test with multiple curly braces"},
{"Message with {Value between brackets} and {{Value between double brackets}}", "Message with {{{0}}} and {{{{{1}}}}}", new Object[]{"Value between brackets", "Value between double brackets"},"Test with multiple curly braces"},
{"Lets go wild 'value 1', {value 2}, '{value 3}', {'{value 4}'}", "Lets go wild '{0}', {{{1}}}, '{{{2}}}', {{'{{{3}}}'}}", new Object[]{"value 1", "value 2","value 3","value 4"},"Some of all"},
{"{'{value}'}", "{{'{{{0}}}'}}", new Object[]{"value"},"Mix om multiple brackets and quotes 1"},
{"'{value}'", "'{{{0}}}'", new Object[]{"value"},"Mix om multiple brackets and quotes 1"},
{"a '{'{123}'}''' b", "a '{{'{{{0}}}'}}''' b", new Object[]{123},"Mix om multiple brackets and quotes 1"},
});
}

@Test
public void testFormatting() {
Assert.assertEquals(expectedResult, MessageFormatUtil.format(pattern, arguments));
}

}

0 comments on commit d5b0e93

Please sign in to comment.