Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,7 @@ protected Object functionStringLength(final EvalContext context) {
assertArgCount(1);
s = InfoSetUtil.stringValue(getArg1().computeValue(context));
}
return Double.valueOf(s.length());
return Double.valueOf(s.codePointCount(0, s.length()));
}

/**
Expand All @@ -665,19 +665,20 @@ protected Object functionSubstring(final EvalContext context) {
assertArgRange(minArgs, maxArgs);
final int ac = getArgumentCount();
final String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
final int s1Length = s1.codePointCount(0, s1.length());
double from = InfoSetUtil.doubleValue(getArg2().computeValue(context));
if (Double.isNaN(from)) {
return "";
}
from = Math.round(from);
if (from > s1.length() + 1) {
if (from > s1Length + 1) {
return "";
}
if (ac == 2) {
if (from < 1) {
from = 1;
}
return s1.substring((int) from - 1);
return s1.substring(s1.offsetByCodePoints(0, (int) from - 1));
}
double length = InfoSetUtil.doubleValue(getArg3().computeValue(context));
length = Math.round(length);
Expand All @@ -688,16 +689,16 @@ protected Object functionSubstring(final EvalContext context) {
if (to < 1) {
return "";
}
if (to > s1.length() + 1) {
if (to > s1Length + 1) {
if (from < 1) {
from = 1;
}
return s1.substring((int) from - 1);
return s1.substring(s1.offsetByCodePoints(0, (int) from - 1));
}
if (from < 1) {
from = 1;
}
return s1.substring((int) from - 1, (int) (to - 1));
return s1.substring(s1.offsetByCodePoints(0, (int) from - 1), s1.offsetByCodePoints(0, (int) (to - 1)));
}

/**
Expand Down Expand Up @@ -770,20 +771,27 @@ protected Object functionTranslate(final EvalContext context) {
final String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
final String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
final String s3 = InfoSetUtil.stringValue(getArg3().computeValue(context));
final char[] chars = s1.toCharArray();
int out = 0;
for (int in = 0; in < chars.length; in++) {
final char c = chars[in];
final int inx = s2.indexOf(c);
final int[] codePoints = s1.codePoints().toArray();
final int[] fromCodePoints = s2.codePoints().toArray();
final int[] toCodePoints = s3.codePoints().toArray();
final StringBuilder buffer = new StringBuilder(s1.length());
for (final int c : codePoints) {
int inx = -1;
for (int i = 0; i < fromCodePoints.length; i++) {
if (fromCodePoints[i] == c) {
inx = i;
break;
}
}
if (inx != -1) {
if (inx < s3.length()) {
chars[out++] = s3.charAt(inx);
if (inx < toCodePoints.length) {
buffer.appendCodePoint(toCodePoints[inx]);
}
} else {
chars[out++] = c;
buffer.appendCodePoint(c);
}
}
return new String(chars, 0, out);
return buffer.toString();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,21 @@ void testNumberConversionIsXPathConformant() {
assertXPathValue(context, "'1e3' = 1000", Boolean.FALSE);
}

@Test
void testStringFunctionsCountCodePoints() {
// U+1D11E MUSICAL SYMBOL G CLEF is one XPath character but two UTF-16 chars.
context.getVariables().declareVariable("clef", "𝄞abc");
assertXPathValue(context, "string-length($clef)", Double.valueOf(4));
assertXPathValue(context, "substring($clef, 2)", "abc");
assertXPathValue(context, "substring($clef, 1, 1)", "𝄞");
assertXPathValue(context, "substring($clef, 2, 2)", "ab");
assertXPathValue(context, "substring($clef, 0, 2)", "𝄞");
assertXPathValue(context, "substring($clef, 5)", "");
assertXPathValue(context, "translate($clef, 'a', '-')", "𝄞-bc");
assertXPathValue(context, "translate($clef, substring($clef, 1, 1), 'x')", "xabc");
assertXPathValue(context, "translate('abc', 'b', $clef)", "a𝄞c");
}

@Test
void testExtendedKeyFunction() {
context.setKeyManager(new ExtendedKeyManager() {
Expand Down