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 @@ -19,6 +19,7 @@
package org.apache.maven.impl.model.profile;

import java.util.List;
import java.util.Locale;

import org.apache.maven.api.services.InterpolatorException;
import org.apache.maven.api.services.ModelBuilderException;
Expand Down Expand Up @@ -65,6 +66,9 @@ public Object length(List<Object> args) {

/**
* Converts the given string to uppercase.
* <p>
* The conversion uses {@link Locale#ROOT} so that profile activation does not depend on the
* default locale of the machine running the build.
*
* @param args A list containing a single string argument
* @return The uppercase version of the input string
Expand All @@ -75,11 +79,14 @@ public Object upper(List<Object> args) {
throw new IllegalArgumentException("upper function requires exactly one argument");
}
String s = ConditionParser.toString(args.get(0));
return s.toUpperCase();
return s.toUpperCase(Locale.ROOT);
}

/**
* Converts the given string to lowercase.
* <p>
* The conversion uses {@link Locale#ROOT} so that profile activation does not depend on the
* default locale of the machine running the build.
*
* @param args A list containing a single string argument
* @return The lowercase version of the input string
Expand All @@ -90,7 +97,7 @@ public Object lower(List<Object> args) {
throw new IllegalArgumentException("lower function requires exactly one argument");
}
String s = ConditionParser.toString(args.get(0));
return s.toLowerCase();
return s.toLowerCase(Locale.ROOT);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.UnaryOperator;

Expand Down Expand Up @@ -554,6 +555,11 @@ private static Object compare(Object left, String operator, Object right) {
* Converts an object to a string representation.
* If the object is a {@code Double}, it formats it without any decimal places.
* Otherwise, it uses the {@code String.valueOf} method.
* <p>
* Formatting uses {@link Locale#ROOT} so that profile activation does not depend on the
* default locale of the machine running the build. Without it, a locale whose default
* numbering system is not latin (for example {@code hi-IN-u-nu-deva}) would render
* {@code 42} as {@code ४२}.
*
* @param value the object to convert to a string
* @return the string representation of the object
Expand All @@ -562,7 +568,7 @@ public static String toString(Object value) {
if (value instanceof Double || value instanceof Float) {
double doubleValue = ((Number) value).doubleValue();
if (doubleValue == Math.floor(doubleValue) && !Double.isInfinite(doubleValue)) {
return String.format("%.0f", doubleValue);
return String.format(Locale.ROOT, "%.0f", doubleValue);
}
}
return String.valueOf(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package org.apache.maven.impl.model.profile;

import java.util.Locale;
import java.util.Map;
import java.util.function.UnaryOperator;

Expand Down Expand Up @@ -94,6 +95,49 @@ void testCaseConversionFunctions() {
assertEquals("world", parser.parse("lower('WORLD')"));
}

/**
* The result of {@code upper()} and {@code lower()} must not depend on the default locale of
* the machine running the build. In the Turkish locale the default case mapping turns
* {@code i} into the dotted {@code İ} and {@code I} into the dotless {@code ı}, which would
* silently change whether a profile is activated.
*/
@Test
void testCaseConversionFunctionsAreLocaleIndependent() {
Locale orig = Locale.getDefault();
try {
Locale[] locales = {Locale.ENGLISH, new Locale("tr")};
for (Locale locale : locales) {
Locale.setDefault(locale);
assertEquals("WINDOWS", parser.parse("upper('windows')"), "upper() in " + locale);
assertEquals("LINUX", parser.parse("upper('linux')"), "upper() in " + locale);
assertEquals("linux", parser.parse("lower('LINUX')"), "lower() in " + locale);
assertEquals("ci", parser.parse("lower('CI')"), "lower() in " + locale);
}
} finally {
Locale.setDefault(orig);
}
}

/**
* Numbers must render with latin digits regardless of the default locale, otherwise a
* condition comparing against a string literal would evaluate differently on a machine
* whose locale uses a non-latin numbering system.
*/
@Test
void testNumberFormattingIsLocaleIndependent() {
Locale orig = Locale.getDefault();
try {
// this locale's default numbering system is Devanagari rather than latin
Locale.setDefault(Locale.forLanguageTag("hi-IN-u-nu-deva"));
assertEquals("42", ConditionParser.toString(42.0));
assertEquals("42", ConditionParser.toString(42.0f));
assertEquals("The answer is 42", parser.parse("'The answer is ' + 42.0"));
assertEquals(2, parser.parse("length(42.0)"));
} finally {
Locale.setDefault(orig);
}
}

@Test
void testConcatFunction() {
assertEquals("HelloWorld", parser.parse("'Hello' + 'World'"));
Expand Down
Loading