Skip to content

Commit

Permalink
Replaced mapValues with an str that accepts a Function<Object, String>
Browse files Browse the repository at this point in the history
  • Loading branch information
JimLaskey committed Mar 18, 2024
1 parent a9ed500 commit 28157d2
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 64 deletions.
130 changes: 97 additions & 33 deletions src/java.base/share/classes/java/lang/StringTemplate.java
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,12 @@ public List<Object> values() {
* StringTemplate st = "The student \{student} is in \{teacher}'s classroom.";
* String result = st.str(); // @highlight substring="str()"
*}
* In the above example, the value of {@code result} will be
* In the above example, the value of {@code result} will be
* {@code "The student Mary is in Johnson's classroom."}. This is
* produced by the interleaving concatenation of fragments and values from the supplied
* {@link StringTemplate}. To accommodate concatenation, values are converted to strings
* as if invoking {@link String#valueOf(Object)}.
* as if invoking {@link String#valueOf(Object)}, with the exception of {@link StringTemplate}
* objects which are recursively converted to Strings with this method.
*
* @return interpolation of this {@link StringTemplate}
*/
Expand All @@ -189,7 +190,34 @@ private String str() {
} catch (RuntimeException | Error ex) {
throw ex;
} catch (Throwable ex) {
throw new RuntimeException("string template join failure", ex);
throw new RuntimeException("string template str failure", ex);
}
}

/**
* Returns the string interpolation of the fragments and values for this
* {@link StringTemplate} after the values have been mapped by {@code func}.
* {@snippet lang = java:
* String student = "Mary";
* String teacher = "Johnson";
* StringTemplate st = "The student \{student} is in \{teacher}'s classroom.";
* String result = st.str(v -> String.valueOf(v).toUpperCase()); // @highlight substring="str()"}
*
* In the above example, the value of {@code result} will be
* {@code "The student MARY is in JOHNSON's classroom."}. This is
* produced by the interleaving concatenation of fragments and strings of values mapped
* through {@code func}.
*
* @return mapped interpolation of this {@link StringTemplate}
*/
private String str(Function<Object, String> func) {
try {
List<String> results = values().stream().map(func).toList();
return (String)sharedData.concatMH().invokeWithArguments(results);
} catch (RuntimeException | Error ex) {
throw ex;
} catch (Throwable ex) {
throw new RuntimeException("string template str failure", ex);
}
}

Expand All @@ -206,7 +234,8 @@ private String str() {
* {@code "The student Mary is in Johnson's classroom."}. This is
* produced by the interleaving concatenation of fragments and values from the supplied
* {@link StringTemplate}. To accommodate concatenation, values are converted to strings
* as if invoking {@link String#valueOf(Object)}.
* as if invoking {@link String#valueOf(Object)}, with the exception of {@link StringTemplate}
* objects which are recursively converted to Strings with this method.
*
* @param stringTemplate target {@link StringTemplate}
* @return interpolation of this {@link StringTemplate}
Expand All @@ -220,6 +249,36 @@ public static String str(StringTemplate stringTemplate) {
return stringTemplate.str();
}

/**
* Returns the string interpolation of the fragments and values for the specified
* {@link StringTemplate} after the values have been mapped through {@code func}.
* {@snippet lang = java:
* String student = "Mary";
* String teacher = "Johnson";
* StringTemplate st = "The student \{student} is in \{teacher}'s classroom.";
* String result = StringTemplate.str(st); // @highlight substring="str()"
*}
* String result = st.str(v -> String.valueOf(v).toUpperCase()); // @highlight substring="str()"}
*
* In the above example, the value of {@code result} will be
* {@code "The student MARY is in JOHNSON's classroom."}. This is
* produced by the interleaving concatenation of fragments and strings of values mapped
* through {@code func}.
*
* @param stringTemplate target {@link StringTemplate}
* @param func function that maps values to strings
* @return interpolation of this {@link StringTemplate}
*
* @throws NullPointerException if stringTemplate is null
*
* @implSpec The implementation returns the result of invoking {@code stringTemplate.str()}.
*/
public static String str(StringTemplate stringTemplate, Function<Object, String> func) {
Objects.requireNonNull(stringTemplate, "stringTemplate should not be null");
Objects.requireNonNull(func, "func should not be null");
return stringTemplate.str(func);
}

/**
* Produces a diagnostic string that describes the fragments and values of this
* {@link StringTemplate}.
Expand Down Expand Up @@ -411,31 +470,6 @@ public static StringTemplate combine(boolean flatten, List<StringTemplate> strin
return combineST(flatten, stringTemplates.toArray(StringTemplate[]::new));
}

/**
* Constructs a new {@link StringTemplate} using this instance's fragments and
* values mapped from this instance's values by the specified
* {@code mapper} function.
* {@snippet lang = java:
* StringTemplate st2 = st1.mapValue(v -> {
* if (v instanceof Supplier<?> s) {
* return s.get();
* }
* return v;
* });
* }
*
* @param mapper mapper function
* @return new {@link StringTemplate}
*/
public StringTemplate mapValues(Function<Object, Object> mapper) {
Objects.requireNonNull(mapper, "mapper must not be null");
List<Object> values = values()
.stream()
.map(mapper)
.toList();
return Factory.createStringTemplate(fragments(), values);
}

/**
* Test this {@link StringTemplate} against another {@link StringTemplate} for equality.
*
Expand Down Expand Up @@ -511,6 +545,14 @@ private static final class SharedData {
@Stable
private final MethodHandle joinMH;

/**
* {@link MethodHandle} Used by {@link StringTemplate#str(StringTemplate, Function)} to produce a string.
* This {@link MethodHandle} is shared by all instances created at the
* {@link java.lang.invoke.CallSite CallSite}.
*/
@Stable
private final MethodHandle concatMH;

/**
* Owner of metadata. Metadata is used to cache information at a
* {@link java.lang.invoke.CallSite CallSite} by a processor. Only one
Expand All @@ -533,16 +575,17 @@ private static final class SharedData {
* @param type {@link MethodType} at callsite
* @param valuesMH {@link MethodHandle} to produce list of values
* @param joinMH {@link MethodHandle} to produce interpolation
* @param concatMH {@link MethodHandle} to produce mapped interpolation
*/
SharedData(List<String> fragments, MethodType type, List<Class<?>> types,
MethodHandle valuesMH, MethodHandle joinMH) {
MethodHandle valuesMH, MethodHandle joinMH, MethodHandle concatMH) {
this.fragments = fragments;
this.type = type;
this.valuesMH = valuesMH;
this.joinMH = joinMH;
this.concatMH = concatMH;
this.owner = null;
this.metaData = null;

}

/**
Expand Down Expand Up @@ -573,6 +616,14 @@ MethodHandle joinMH() {
return joinMH;
}

/**
* {@return MethodHandle to produce mapped interpolation }
*/
MethodHandle concatMH() {
return concatMH;
}


/**
* Get owner meta data.
*
Expand Down Expand Up @@ -675,7 +726,8 @@ private static MethodHandle createStringTemplateMH(List<String> fragments, Metho
List<MethodHandle> filters = filterGetters(getters);
List<Class<?>> ftypes = returnTypes(filters);
MethodHandle joinMH = createJoinMH(fragments, ftypes, filters, permute);
SharedData sharedData = new SharedData(fragments, type, type.parameterList(), valuesMH, joinMH);
MethodHandle concatMH = createConcatMH(fragments);
SharedData sharedData = new SharedData(fragments, type, type.parameterList(), valuesMH, joinMH, concatMH);
MethodHandle constructor = createConstructorMH(type, sharedData, ptypes);
return constructor;
}
Expand Down Expand Up @@ -706,7 +758,8 @@ private static StringTemplate createStringTemplate(List<String> fragments, List<
List<MethodHandle> filters = filterGetters(getters);
List<Class<?>> ftypes = returnTypes(filters);
MethodHandle joinMH = createJoinMH(fragments, ftypes, filters, permute);
sharedData = new SharedData(fragments, type, type.parameterList(), valuesMH, joinMH);
MethodHandle concatMH = createConcatMH(fragments);
sharedData = new SharedData(fragments, type, type.parameterList(), valuesMH, joinMH, concatMH);
sharedDataMap.put(fragments, sharedData);
return new StringTemplate(values, sharedData);
}
Expand Down Expand Up @@ -769,6 +822,17 @@ private static MethodHandle createJoinMH(List<String> fragments, List<Class<?>>
}
}

private static MethodHandle createConcatMH(List<String> fragments) {
try {
Class<?>[] ptypes = new Class<?>[fragments.size() - 1];
Arrays.fill(ptypes, String.class);
MethodHandle concatMH = StringConcatFactory.makeConcatWithTemplate(fragments, List.of(ptypes));
return concatMH;
} catch (StringConcatException ex) {
throw new InternalError("constructing internal string template", ex);
}
}

/**
* Create a {@link StringTemplate} constructor.
*
Expand Down
39 changes: 8 additions & 31 deletions test/jdk/java/lang/template/Basic.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ public static void main(String... arg) {
componentTests();
limitsTests();
stringTemplateCoverage();
mapTests();
}

static void ASSERT(String a, String b) {
Expand Down Expand Up @@ -164,36 +163,14 @@ static void stringTemplateCoverage() {
StringTemplate shape = "\{"triangle"}";
StringTemplate statement = "This is a \{color} \{shape}.";
ASSERT(str(StringTemplate.combine(true, statement)), "This is a red triangle.");
ASSERT(str(statement, v -> {
if (v instanceof StringTemplate st) {
return str(st).toUpperCase();

} else {
return String.valueOf(v).toUpperCase();
}
}), "This is a RED TRIANGLE.");
}


/*
* mapValues
*/
static void mapTests() {
int x = 10, y = 20;
StringTemplate st = "The sum of \{x} and \{y} equals \{x + y}";
StringTemplate st1 = st.mapValues(v -> v instanceof Integer i ? i * 100 : v);
ASSERT(str(st1), "The sum of 1000 and 2000 equals 3000");
}




















}

0 comments on commit 28157d2

Please sign in to comment.