Skip to content

Commit

Permalink
Add flattening to st combine
Browse files Browse the repository at this point in the history
  • Loading branch information
JimLaskey committed Mar 12, 2024
1 parent 1036c04 commit b9b913b
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 53 deletions.
20 changes: 12 additions & 8 deletions src/java.base/share/classes/java/lang/StringTemplate.java
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ static String join(List<String> fragments, List<?> values) {
* Combine zero or more {@link StringTemplate StringTemplates} into a single
* {@link StringTemplate}.
* {@snippet :
* StringTemplate st = StringTemplate.combine("\{a}", "\{b}", "\{c}");
* StringTemplate st = StringTemplate.combine(false, "\{a}", "\{b}", "\{c}");
* assert st.join().equals("\{a}\{b}\{c}".join());
* }
* Fragment lists from the {@link StringTemplate StringTemplates} are combined end to
Expand All @@ -306,7 +306,7 @@ static String join(List<String> fragments, List<?> values) {
* StringTemplate st1 = "a\{""}b\{""}c";
* StringTemplate st2 = "x\{""}y\{""}z";
* StringTemplate st3 = "a\{""}b\{""}cx\{""}y\{""}z";
* StringTemplate stc = StringTemplate.combine(st1, st2);
* StringTemplate stc = StringTemplate.combine(false, st1, st2);
*
* assert Objects.equals(st1.fragments(), List.of("a", "b", "c"));
* assert Objects.equals(st2.fragments(), List.of("x", "y", "z"));
Expand All @@ -318,6 +318,8 @@ static String join(List<String> fragments, List<?> values) {
* n is the total of number of values across all the supplied
* {@link StringTemplate StringTemplates}.
*
* @param flatten if true will flatten nested {@link StringTemplate StringTemplates} into the
* combination
* @param stringTemplates zero or more {@link StringTemplate}
*
* @return combined {@link StringTemplate}
Expand All @@ -330,16 +332,16 @@ static String join(List<String> fragments, List<?> values) {
* <code>StringTemplate.of("")</code> . If only one {@link StringTemplate} argument is provided
* then it is returned unchanged.
*/
static StringTemplate combine(StringTemplate... stringTemplates) {
static StringTemplate combine(boolean flatten, StringTemplate... stringTemplates) {
JavaTemplateAccess JTA = SharedSecrets.getJavaTemplateAccess();
return JTA.combine(stringTemplates);
return JTA.combine(flatten, stringTemplates);
}

/**
* Combine a list of {@link StringTemplate StringTemplates} into a single
* {@link StringTemplate}.
* {@snippet :
* StringTemplate st = StringTemplate.combine(List.of("\{a}", "\{b}", "\{c}"));
* StringTemplate st = StringTemplate.combine(false, List.of("\{a}", "\{b}", "\{c}"));
* assert st.join().equals("\{a}\{b}\{c}".join());
* }
* Fragment lists from the {@link StringTemplate StringTemplates} are combined end to
Expand All @@ -358,7 +360,7 @@ static StringTemplate combine(StringTemplate... stringTemplates) {
* StringTemplate st1 = "a\{""}b\{""}c";
* StringTemplate st2 = "x\{""}y\{""}z";
* StringTemplate st3 = "a\{""}b\{""}cx\{""}y\{""}z";
* StringTemplate stc = StringTemplate.combine(List.of(st1, st2));
* StringTemplate stc = StringTemplate.combine(false, List.of(st1, st2));
*
* assert Objects.equals(st1.fragments(), List.of("a", "b", "c"));
* assert Objects.equals(st2.fragments(), List.of("x", "y", "z"));
Expand All @@ -370,6 +372,8 @@ static StringTemplate combine(StringTemplate... stringTemplates) {
* n is the total of number of values across all the supplied
* {@link StringTemplate StringTemplates}.
*
* @param flatten if true will flatten nested {@link StringTemplate StringTemplates} into the
* combination
* @param stringTemplates list of {@link StringTemplate}
*
* @return combined {@link StringTemplate}
Expand All @@ -382,9 +386,9 @@ static StringTemplate combine(StringTemplate... stringTemplates) {
* <code>StringTemplate.of("")</code> . If {@code stringTemplates.size() == 1}
* then the first element of the list is returned unchanged.
*/
static StringTemplate combine(List<StringTemplate> stringTemplates) {
static StringTemplate combine(boolean flatten, List<StringTemplate> stringTemplates) {
JavaTemplateAccess JTA = SharedSecrets.getJavaTemplateAccess();
return JTA.combine(stringTemplates.toArray(new StringTemplate[0]));
return JTA.combine(flatten, stringTemplates.toArray(StringTemplate[]::new));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,7 @@ MethodHandle joinMH() {
*
* @param owner owner object, should be unique to the processor
* @param supplier supplier of meta data
* @return meta data
*
* @param <S> type of owner
* @param <T> type of meta data
* @return meta data or null if it fails to win the cache
*/
@SuppressWarnings("unchecked")
<S, T> T getMetaData(S owner, Supplier<T> supplier) {
Expand Down
56 changes: 37 additions & 19 deletions src/java.base/share/classes/java/lang/runtime/TemplateSupport.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
Expand Down Expand Up @@ -121,41 +122,58 @@ public String join(List<String> fragments, List<?> values) {
* assert st.join().equals("\{a}\{b}\{c}");
* }
*
* @param sts zero or more {@link StringTemplate StringTemplates}
* @param flatten if true will flatten nested {@link StringTemplate StringTemplates} into the
* combination
* @param sts zero or more {@link StringTemplate StringTemplates}
*
* @return combined {@link StringTemplate}
*
* @throws NullPointerException if sts is null or if any element of sts is null
*/
@Override
public StringTemplate combine(StringTemplate... sts) {
public StringTemplate combine(boolean flatten, StringTemplate... sts) {
Objects.requireNonNull(sts, "sts must not be null");
if (sts.length == 0) {
return StringTemplate.of("");
} else if (sts.length == 1) {
} else if (sts.length == 1 && !flatten) {
return Objects.requireNonNull(sts[0], "string templates should not be null");
}
int size = 0;
List<String> fragments = new ArrayList<>();
List<Object> values = new ArrayList<>();
for (StringTemplate st : sts) {
Objects.requireNonNull(st, "string templates should not be null");
size += st.values().size();
flatten(flatten, st, fragments, values);
}
String[] combinedFragments = new String[size + 1];
Object[] combinedValues = new Object[size];
combinedFragments[0] = "";
int fragmentIndex = 1;
int valueIndex = 0;
for (StringTemplate st : sts) {
Iterator<String> iterator = st.fragments().iterator();
combinedFragments[fragmentIndex - 1] += iterator.next();
while (iterator.hasNext()) {
combinedFragments[fragmentIndex++] = iterator.next();
}
for (Object value : st.values()) {
combinedValues[valueIndex++] = value;
return of(fragments, values);
}

/**
* Recursively combining the specified {@link StringTemplate} to the mix.
*
* @param flatten if true will flatten nested {@link StringTemplate StringTemplates} into the
* combination
* @param st specified {@link StringTemplate}
* @param fragments accumulation of fragments
* @param values accumulation of values
*/
private static void flatten(boolean flatten, StringTemplate st, List<String> fragments, List<Object> values) {
Iterator<String> fragmentsIter = st.fragments().iterator();
if (fragments.isEmpty()) {
fragments.add(fragmentsIter.next());
} else {
int last = fragments.size() - 1;
fragments.set(last, fragments.get(last) + fragmentsIter.next());
}
for(Object value : st.values()) {
if (flatten && value instanceof StringTemplate nested) {
flatten(true, nested, fragments, values);
int last = fragments.size() - 1;
fragments.set(last, fragments.get(last) + fragmentsIter.next());
} else {
values.add(value);
fragments.add(fragmentsIter.next());
}
}
return StringTemplateImplFactory.newTrustedStringTemplate(combinedFragments, combinedValues);
}

@Override
Expand Down
9 changes: 5 additions & 4 deletions src/java.base/share/classes/java/util/FormatterBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public static MethodHandle create(String format, Locale locale, Class<?>[] ptype
public static MethodHandle create(String format, Class<?>[] ptypes) {
Objects.requireNonNull(format, "format must not be null");
Objects.requireNonNull(ptypes, "ptypes must not be null");
return new FormatterBuilder(format, Locale.ROOT, ptypes).build();
return new FormatterBuilder(format, Locale.getDefault(), ptypes).build();
}

private final static JavaTemplateAccess JTA = SharedSecrets.getJavaTemplateAccess();
Expand Down Expand Up @@ -178,7 +178,7 @@ public static MethodHandle create(StringTemplate st, Locale locale) {
*/
public static MethodHandle create(StringTemplate st) {
Objects.requireNonNull(st, "st must not be null");
return create(st, Locale.ROOT);
return create(st, Locale.getDefault());
}

private FormatterBuilder(String format, Locale locale, Class<?>[] ptypes) {
Expand Down Expand Up @@ -638,8 +638,9 @@ private static class FormatterBuilderMetaData extends StringTemplateMetaData {
* @param l Locale
*/
FormatterBuilderMetaData(StringTemplate st, Locale l) {
super(st);
this.l = l;
super();
this.l = l != null ? l : Locale.getDefault();

}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,13 @@ public interface JavaTemplateAccess {
* assert st.join().equals("\{a}\{b}\{c}");
* }
*
* @param sts zero or more {@link StringTemplate}
* @param flatten if true will flatten nested {@link StringTemplate StringTemplates} into the
* combination
* @param sts zero or more {@link StringTemplate}
* @return combined {@link StringTemplate}
* @throws NullPointerException if sts is null or if any element of sts is null
*/
StringTemplate combine(StringTemplate... sts);
StringTemplate combine(boolean flatten, StringTemplate... sts);


/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.lang.invoke.StringConcatFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;

/**
Expand All @@ -51,10 +52,8 @@ public class StringTemplateMetaData {

/**
* Public constructor.
*
* @param st associated {@link StringTemplate}
*/
public StringTemplateMetaData(StringTemplate st) {
public StringTemplateMetaData() {
this.methodHandle = null;
}

Expand All @@ -77,9 +76,10 @@ public void setMethodHandle(MethodHandle methodHandle) {
/**
* Construct the {@link MethodHandle}. Override if special handling is required.
* @param st associated {@link StringTemplate}
* @return
* @return constructed {@link MethodHandle}
*/
public MethodHandle createMethodHandle(StringTemplate st) {
Objects.requireNonNull(st, "st must not be null");
List<String> fragments = filterFragments(st.fragments());
List<Class<?>> ptypes = JTA.getTypes(st);
MethodHandle[] filters = createValueFilters(ptypes);
Expand All @@ -105,6 +105,7 @@ public MethodHandle createMethodHandle(StringTemplate st) {
* @return filtered fragments
*/
public List<String> filterFragments(List<String> fragments) {
Objects.requireNonNull(fragments, "fragments must not be null");
return fragments;
}

Expand All @@ -118,6 +119,7 @@ public List<String> filterFragments(List<String> fragments) {
* a value needs no filtering.
*/
public MethodHandle[] createValueFilters(List<Class<?>> ptypes) {
Objects.requireNonNull(ptypes, "ptypes must not be null");
return new MethodHandle[ptypes.size()];
}

Expand All @@ -130,6 +132,8 @@ public MethodHandle[] createValueFilters(List<Class<?>> ptypes) {
* @return {@link MethodHandle} that performs the basic concat.
*/
public MethodHandle createConcat(List<String> fragments, List<Class<?>> ptypes) {
Objects.requireNonNull(fragments, "fragments must not be null");
Objects.requireNonNull(ptypes, "ptypes must not be null");
MethodHandle mh;
try {
mh = StringConcatFactory.makeConcatWithTemplate(fragments, ptypes);
Expand All @@ -139,15 +143,23 @@ public MethodHandle createConcat(List<String> fragments, List<Class<?>> ptypes)
return mh;
}

/**
* invoke the constructed {@link MethodHandle} to return the processor's string result.
* May return null if
*
* @param st associated {@link StringTemplate}
* @return the processor's string result
*/
public String invoke(StringTemplate st) {
if (methodHandle != null) {
try {
return (String)methodHandle.invokeExact(st);
} catch (Throwable e) {
throw new InternalError(e);
}
}
return null;
Objects.requireNonNull(st, "st must not be null");
if (methodHandle != null) {
try {
return (String)methodHandle.invokeExact(st);
} catch (Throwable e) {
throw new InternalError(e);
}
}
return null;
}

/**
Expand All @@ -161,6 +173,9 @@ public String invoke(StringTemplate st) {
* @return instance of the metadata class.
*/
public static <M extends StringTemplateMetaData> M getMetaData(StringTemplate st, Object owner, Supplier<M> supplier) {
Objects.requireNonNull(st, "st must not be null");
Objects.requireNonNull(owner, "owner must not be null");
Objects.requireNonNull(supplier, "supplier must not be null");
if (JTA.isLiteral(st)) {
M metaData = JTA.getMetaData(st, owner, () -> {
M md = supplier.get();
Expand Down
12 changes: 8 additions & 4 deletions test/jdk/java/lang/template/Basic.java
Original file line number Diff line number Diff line change
Expand Up @@ -382,14 +382,18 @@ static void stringTemplateCoverage() {
ASSERT(tsValues.fragments(), List.of("", " + ", " = ", ""));
ASSERT(tsValues.values(), List.of(x, y, x + y));
ASSERT(tsValues.join(), x + " + " + y + " = " + (x + y));
ASSERT(StringTemplate.combine(src, src).join(),
ASSERT(StringTemplate.combine(false, src, src).join(),
"\{x} + \{y} = \{x + y}\{x} + \{y} = \{x + y}".join());
ASSERT(StringTemplate.combine(src), src);
ASSERT(StringTemplate.combine().join(), "");
ASSERT(StringTemplate.combine(List.of(src, src)).join(),
ASSERT(StringTemplate.combine(false, src), src);
ASSERT(StringTemplate.combine(false).join(), "");
ASSERT(StringTemplate.combine(false, List.of(src, src)).join(),
"\{x} + \{y} = \{x + y}\{x} + \{y} = \{x + y}".join());
ASSERT(StringTemplate.join(src), x + " + " + y + " = " + (x + y));
ASSERT("a string".asTemplate(), "\{}a string");
StringTemplate color = "\{"red"}";
StringTemplate shape = "\{"triangle"}";
StringTemplate statement = "This is a \{color} \{shape}.";
ASSERT(StringTemplate.combine(true, statement).join(), "This is a red triangle.");
}

/*
Expand Down

0 comments on commit b9b913b

Please sign in to comment.