mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 23:04:50 +02:00
8285932: Implementation of JEP 430 String Templates (Preview)
Reviewed-by: mcimadamore, rriggs, darcy
This commit is contained in:
parent
da2c930262
commit
4aa65cbeef
74 changed files with 9309 additions and 99 deletions
1004
src/java.base/share/classes/java/lang/runtime/Carriers.java
Normal file
1004
src/java.base/share/classes/java/lang/runtime/Carriers.java
Normal file
File diff suppressed because it is too large
Load diff
230
src/java.base/share/classes/java/lang/runtime/ReferenceKey.java
Normal file
230
src/java.base/share/classes/java/lang/runtime/ReferenceKey.java
Normal file
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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 General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package java.lang.runtime;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* View/wrapper of keys used by the backing {@link ReferencedKeyMap}.
|
||||
* There are two style of keys; one for entries in the backing map and
|
||||
* one for queries to the backing map. This second style avoids the
|
||||
* overhead of a {@link Reference} object.
|
||||
*
|
||||
* @param <T> key type
|
||||
*
|
||||
* @since 21
|
||||
*
|
||||
* Warning: This class is part of PreviewFeature.Feature.STRING_TEMPLATES.
|
||||
* Do not rely on its availability.
|
||||
*/
|
||||
interface ReferenceKey<T> {
|
||||
/**
|
||||
* {@return the value of the unwrapped key}
|
||||
*/
|
||||
T get();
|
||||
|
||||
/**
|
||||
* Cleanup unused key.
|
||||
*/
|
||||
void unused();
|
||||
|
||||
/**
|
||||
* {@link WeakReference} wrapper key for entries in the backing map.
|
||||
*
|
||||
* @param <T> key type
|
||||
*
|
||||
* @since 21
|
||||
*/
|
||||
class WeakKey<T> extends WeakReference<T> implements ReferenceKey<T> {
|
||||
/**
|
||||
* Saved hashcode of the key. Used when {@link WeakReference} is
|
||||
* null.
|
||||
*/
|
||||
private final int hashcode;
|
||||
|
||||
/**
|
||||
* Private constructor.
|
||||
*
|
||||
* @param key unwrapped key value
|
||||
* @param queue reference queue
|
||||
*/
|
||||
WeakKey(T key, ReferenceQueue<T> queue) {
|
||||
super(key, queue);
|
||||
this.hashcode = Objects.hashCode(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup unused key. No need to enqueue since the key did not make it
|
||||
* into the map.
|
||||
*/
|
||||
@Override
|
||||
public void unused() {
|
||||
clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
// Necessary when removing a null reference
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
// Necessary when comparing an unwrapped key
|
||||
if (obj instanceof ReferenceKey<?> key) {
|
||||
obj = key.get();
|
||||
}
|
||||
return Objects.equals(get(), obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// Use saved hashcode
|
||||
return hashcode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getCanonicalName() + "#" + System.identityHashCode(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link SoftReference} wrapper key for entries in the backing map.
|
||||
*
|
||||
* @param <T> key type
|
||||
*
|
||||
* @since 21
|
||||
*/
|
||||
class SoftKey<T> extends SoftReference<T> implements ReferenceKey<T> {
|
||||
/**
|
||||
* Saved hashcode of the key. Used when {@link SoftReference} is
|
||||
* null.
|
||||
*/
|
||||
private final int hashcode;
|
||||
|
||||
/**
|
||||
* Private constructor.
|
||||
*
|
||||
* @param key unwrapped key value
|
||||
* @param queue reference queue
|
||||
*/
|
||||
SoftKey(T key, ReferenceQueue<T> queue) {
|
||||
super(key, queue);
|
||||
this.hashcode = Objects.hashCode(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup unused key. No need to enqueue since the key did not make it
|
||||
* into the map.
|
||||
*/
|
||||
@Override
|
||||
public void unused() {
|
||||
clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
// Necessary when removing a null reference
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
// Necessary when comparing an unwrapped key
|
||||
if (obj instanceof ReferenceKey<?> key) {
|
||||
obj = key.get();
|
||||
}
|
||||
return Objects.equals(get(), obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// Use saved hashcode
|
||||
return hashcode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getCanonicalName() + "#" + System.identityHashCode(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for querying the backing map. Avoids the overhead of an
|
||||
* {@link Reference} object.
|
||||
*
|
||||
* @param <T> key type
|
||||
*
|
||||
* @since 21
|
||||
*/
|
||||
class StrongKey<T> implements ReferenceKey<T> {
|
||||
T key;
|
||||
|
||||
/**
|
||||
* Private constructor.
|
||||
*
|
||||
* @param key unwrapped key value
|
||||
*/
|
||||
StrongKey(T key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the unwrapped key}
|
||||
*/
|
||||
@Override
|
||||
public T get() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unused() {
|
||||
key = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
// Necessary when comparing an unwrapped key
|
||||
if (obj instanceof ReferenceKey<?> key) {
|
||||
obj = key.get();
|
||||
}
|
||||
return Objects.equals(get(), obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// Use unwrapped key hash code
|
||||
return get().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getCanonicalName() + "#" + System.identityHashCode(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,334 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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 General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package java.lang.runtime;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Objects;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* This class provides management of {@link Map maps} where it is desirable to
|
||||
* remove entries automatically when the key is garbage collected. This is
|
||||
* accomplished by using a backing map where the keys are either a
|
||||
* {@link WeakReference} or a {@link SoftReference}.
|
||||
* <p>
|
||||
* To create a {@link ReferencedKeyMap} the user must provide a {@link Supplier}
|
||||
* of the backing map and whether {@link WeakReference} or
|
||||
* {@link SoftReference} is to be used.
|
||||
*
|
||||
* {@snippet :
|
||||
* // Use HashMap and WeakReference
|
||||
* Map<Long, String> map = ReferencedKeyMap.create(false, HashMap::new);
|
||||
* map.put(10_000_000L, "a");
|
||||
* map.put(10_000_001L, "b");
|
||||
* map.put(10_000_002L, "c");
|
||||
* map.put(10_000_003L, "d");
|
||||
* map.put(10_000_004L, "e");
|
||||
*
|
||||
* // Use ConcurrentHashMap and SoftReference
|
||||
* map = ReferencedKeyMap.create(true, ConcurrentHashMap::new);
|
||||
* map.put(20_000_000L, "v");
|
||||
* map.put(20_000_001L, "w");
|
||||
* map.put(20_000_002L, "x");
|
||||
* map.put(20_000_003L, "y");
|
||||
* map.put(20_000_004L, "z");
|
||||
* }
|
||||
*
|
||||
* @implNote Care must be given that the backing map does replacement by
|
||||
* replacing the value in the map entry instead of deleting the old entry and
|
||||
* adding a new entry, otherwise replaced entries may end up with a strongly
|
||||
* referenced key. {@link HashMap} and {@link ConcurrentHashMap} are known
|
||||
* to be safe.
|
||||
*
|
||||
* @param <K> the type of keys maintained by this map
|
||||
* @param <V> the type of mapped values
|
||||
*
|
||||
* @since 21
|
||||
*
|
||||
* Warning: This class is part of PreviewFeature.Feature.STRING_TEMPLATES.
|
||||
* Do not rely on its availability.
|
||||
*/
|
||||
final class ReferencedKeyMap<K, V> implements Map<K, V> {
|
||||
/**
|
||||
* true if {@link SoftReference} keys are to be used,
|
||||
* {@link WeakReference} otherwise.
|
||||
*/
|
||||
private final boolean isSoft;
|
||||
|
||||
/**
|
||||
* Backing {@link Map}.
|
||||
*/
|
||||
private final Map<ReferenceKey<K>, V> map;
|
||||
|
||||
/**
|
||||
* {@link ReferenceQueue} for cleaning up {@link ReferenceKey.WeakKey EntryKeys}.
|
||||
*/
|
||||
private final ReferenceQueue<K> stale;
|
||||
|
||||
/**
|
||||
* Private constructor.
|
||||
*
|
||||
* @param isSoft true if {@link SoftReference} keys are to
|
||||
* be used, {@link WeakReference} otherwise.
|
||||
* @param map backing map
|
||||
*/
|
||||
private ReferencedKeyMap(boolean isSoft, Map<ReferenceKey<K>, V> map) {
|
||||
this.isSoft = isSoft;
|
||||
this.map = map;
|
||||
this.stale = new ReferenceQueue<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ReferencedKeyMap} map.
|
||||
*
|
||||
* @param isSoft true if {@link SoftReference} keys are to
|
||||
* be used, {@link WeakReference} otherwise.
|
||||
* @param supplier {@link Supplier} of the backing map
|
||||
*
|
||||
* @return a new map with {@link Reference} keys
|
||||
*
|
||||
* @param <K> the type of keys maintained by the new map
|
||||
* @param <V> the type of mapped values
|
||||
*/
|
||||
static <K, V> ReferencedKeyMap<K, V>
|
||||
create(boolean isSoft, Supplier<Map<ReferenceKey<K>, V>> supplier) {
|
||||
return new ReferencedKeyMap<K, V>(isSoft, supplier.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ReferencedKeyMap} map using
|
||||
* {@link WeakReference} keys.
|
||||
*
|
||||
* @param supplier {@link Supplier} of the backing map
|
||||
*
|
||||
* @return a new map with {@link Reference} keys
|
||||
*
|
||||
* @param <K> the type of keys maintained by the new map
|
||||
* @param <V> the type of mapped values
|
||||
*/
|
||||
static <K, V> ReferencedKeyMap<K, V>
|
||||
create(Supplier<Map<ReferenceKey<K>, V>> supplier) {
|
||||
return new ReferencedKeyMap<K, V>(false, supplier.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a key suitable for a map entry}
|
||||
*
|
||||
* @param key unwrapped key
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private ReferenceKey<K> entryKey(Object key) {
|
||||
if (isSoft) {
|
||||
return new ReferenceKey.SoftKey<>((K)key, stale);
|
||||
} else {
|
||||
return new ReferenceKey.WeakKey<>((K)key, stale);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a key suitable for lookup}
|
||||
*
|
||||
* @param key unwrapped key
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private ReferenceKey<K> lookupKey(Object key) {
|
||||
return new ReferenceKey.StrongKey<>((K)key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
removeStaleReferences();
|
||||
return map.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
removeStaleReferences();
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
Objects.requireNonNull(key, "key must not be null");
|
||||
removeStaleReferences();
|
||||
return map.containsKey(lookupKey(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
Objects.requireNonNull(value, "value must not be null");
|
||||
removeStaleReferences();
|
||||
return map.containsValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
Objects.requireNonNull(key, "key must not be null");
|
||||
removeStaleReferences();
|
||||
return map.get(lookupKey(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K key, V newValue) {
|
||||
Objects.requireNonNull(key, "key must not be null");
|
||||
Objects.requireNonNull(newValue, "value must not be null");
|
||||
removeStaleReferences();
|
||||
ReferenceKey<K> entryKey = entryKey(key);
|
||||
// If {@code put} returns non-null then was actually a {@code replace}
|
||||
// and older key was used. In that case the new key was not used and the
|
||||
// reference marked stale.
|
||||
V oldValue = map.put(entryKey, newValue);
|
||||
if (oldValue != null) {
|
||||
entryKey.unused();
|
||||
}
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(Object key) {
|
||||
// Rely on gc to clean up old key.
|
||||
return map.remove(lookupKey(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends K, ? extends V> m) {
|
||||
removeStaleReferences();
|
||||
for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
|
||||
K key = entry.getKey();
|
||||
V value = entry.getValue();
|
||||
put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
removeStaleReferences();
|
||||
// Rely on gc to clean up old keys.
|
||||
map.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Common routine for collecting the current set of keys.
|
||||
*
|
||||
* @return {@link Stream} of valid keys (unwrapped)
|
||||
*/
|
||||
private Stream<K> filterKeySet() {
|
||||
return map.keySet()
|
||||
.stream()
|
||||
.map(ReferenceKey::get)
|
||||
.filter(Objects::nonNull);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
removeStaleReferences();
|
||||
return filterKeySet().collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
removeStaleReferences();
|
||||
return map.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<K, V>> entrySet() {
|
||||
removeStaleReferences();
|
||||
return filterKeySet()
|
||||
.map(k -> new AbstractMap.SimpleEntry<>(k, get(k)))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public V putIfAbsent(K key, V newValue) {
|
||||
removeStaleReferences();
|
||||
ReferenceKey<K> entryKey = entryKey(key);
|
||||
// If {@code putIfAbsent} returns non-null then was actually a
|
||||
// {@code replace} and older key was used. In that case the new key was
|
||||
// not used and the reference marked stale.
|
||||
V oldValue = map.putIfAbsent(entryKey, newValue);
|
||||
if (oldValue != null) {
|
||||
entryKey.unused();
|
||||
}
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object key, Object value) {
|
||||
// Rely on gc to clean up old key.
|
||||
return map.remove(lookupKey(key), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replace(K key, V oldValue, V newValue) {
|
||||
removeStaleReferences();
|
||||
// If replace is successful then the older key will be used and the
|
||||
// lookup key will suffice.
|
||||
return map.replace(lookupKey(key), oldValue, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V replace(K key, V value) {
|
||||
removeStaleReferences();
|
||||
// If replace is successful then the older key will be used and the
|
||||
// lookup key will suffice.
|
||||
return map.replace(lookupKey(key), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
removeStaleReferences();
|
||||
return filterKeySet()
|
||||
.map(k -> k + "=" + get(k))
|
||||
.collect(Collectors.joining(", ", "{", "}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes enqueued weak references from map.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void removeStaleReferences() {
|
||||
while (true) {
|
||||
ReferenceKey.WeakKey<K> key = (ReferenceKey.WeakKey<K>)stale.poll();
|
||||
if (key == null) {
|
||||
break;
|
||||
}
|
||||
map.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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 General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package java.lang.runtime;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* This class implements specialized {@link StringTemplate StringTemplates} produced by
|
||||
* string template bootstrap method callsites generated by the compiler. Instances of this
|
||||
* class are produced by {@link StringTemplateImplFactory}.
|
||||
* <p>
|
||||
* Values are stored by subclassing {@link Carriers.CarrierObject}. This allows specializations
|
||||
* and sharing of value shapes without creating a new class for each shape.
|
||||
* <p>
|
||||
* {@link StringTemplate} fragments are shared via binding to the
|
||||
* {@link java.lang.invoke.CallSite CallSite's} {@link MethodHandle}.
|
||||
* <p>
|
||||
* The {@link StringTemplateImpl} instance also carries
|
||||
* specialized {@link MethodHandle MethodHandles} for producing the values list and interpolation.
|
||||
* These {@link MethodHandle MethodHandles} are also shared by binding to the
|
||||
* {@link java.lang.invoke.CallSite CallSite}.
|
||||
*
|
||||
* @since 21
|
||||
*
|
||||
* Warning: This class is part of PreviewFeature.Feature.STRING_TEMPLATES.
|
||||
* Do not rely on its availability.
|
||||
*/
|
||||
final class StringTemplateImpl extends Carriers.CarrierObject implements StringTemplate {
|
||||
/**
|
||||
* List of string fragments for the string template. This value of this list is shared by
|
||||
* all instances created at the {@link java.lang.invoke.CallSite CallSite}.
|
||||
*/
|
||||
private final List<String> fragments;
|
||||
|
||||
/**
|
||||
* Specialized {@link MethodHandle} used to implement the {@link StringTemplate StringTemplate's}
|
||||
* {@code values} method. This {@link MethodHandle} is shared by all instances created at the
|
||||
* {@link java.lang.invoke.CallSite CallSite}.
|
||||
*/
|
||||
private final MethodHandle valuesMH;
|
||||
|
||||
/**
|
||||
* Specialized {@link MethodHandle} used to implement the {@link StringTemplate StringTemplate's}
|
||||
* {@code interpolate} method. This {@link MethodHandle} is shared by all instances created at the
|
||||
* {@link java.lang.invoke.CallSite CallSite}.
|
||||
*/
|
||||
private final MethodHandle interpolateMH;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param primitiveCount number of primitive slots required (bound at callsite)
|
||||
* @param objectCount number of object slots required (bound at callsite)
|
||||
* @param fragments list of string fragments (bound in (bound at callsite)
|
||||
* @param valuesMH {@link MethodHandle} to produce list of values (bound at callsite)
|
||||
* @param interpolateMH {@link MethodHandle} to produce interpolation (bound at callsite)
|
||||
*/
|
||||
StringTemplateImpl(int primitiveCount, int objectCount,
|
||||
List<String> fragments, MethodHandle valuesMH, MethodHandle interpolateMH) {
|
||||
super(primitiveCount, objectCount);
|
||||
this.fragments = fragments;
|
||||
this.valuesMH = valuesMH;
|
||||
this.interpolateMH = interpolateMH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> fragments() {
|
||||
return fragments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> values() {
|
||||
try {
|
||||
return (List<Object>)valuesMH.invokeExact(this);
|
||||
} catch (RuntimeException | Error ex) {
|
||||
throw ex;
|
||||
} catch (Throwable ex) {
|
||||
throw new RuntimeException("string template values failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String interpolate() {
|
||||
try {
|
||||
return (String)interpolateMH.invokeExact(this);
|
||||
} catch (RuntimeException | Error ex) {
|
||||
throw ex;
|
||||
} catch (Throwable ex) {
|
||||
throw new RuntimeException("string template interpolate failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof StringTemplate st &&
|
||||
Objects.equals(fragments(), st.fragments()) &&
|
||||
Objects.equals(values(), st.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(fragments(), values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return StringTemplate.toString(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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 General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package java.lang.runtime;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.StringConcatException;
|
||||
import java.lang.invoke.StringConcatFactory;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class synthesizes {@link StringTemplate StringTemplates} based on
|
||||
* fragments and bootstrap method type. Usage is primarily from
|
||||
* {@link java.lang.runtime.TemplateRuntime}.
|
||||
*
|
||||
* @since 21
|
||||
*
|
||||
* Warning: This class is part of PreviewFeature.Feature.STRING_TEMPLATES.
|
||||
* Do not rely on its availability.
|
||||
*/
|
||||
final class StringTemplateImplFactory {
|
||||
|
||||
/**
|
||||
* Private constructor.
|
||||
*/
|
||||
StringTemplateImplFactory() {
|
||||
throw new AssertionError("private constructor");
|
||||
}
|
||||
|
||||
/*
|
||||
* {@link StringTemplateImpl} constructor MethodHandle.
|
||||
*/
|
||||
private static final MethodHandle CONSTRUCTOR;
|
||||
|
||||
|
||||
/*
|
||||
* Frequently used method types.
|
||||
*/
|
||||
private static final MethodType MT_STRING_STIMPL =
|
||||
MethodType.methodType(String.class, StringTemplateImpl.class);
|
||||
private static final MethodType MT_LIST_STIMPL =
|
||||
MethodType.methodType(List.class, StringTemplateImpl.class);
|
||||
|
||||
/**
|
||||
* List (for nullable) of MethodHandle;
|
||||
*/
|
||||
private static final MethodHandle TO_LIST;
|
||||
|
||||
static {
|
||||
try {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
|
||||
MethodType mt = MethodType.methodType(void.class, int.class, int.class, List.class,
|
||||
MethodHandle.class, MethodHandle.class);
|
||||
CONSTRUCTOR = lookup.findConstructor(StringTemplateImpl.class, mt)
|
||||
.asType(mt.changeReturnType(Carriers.CarrierObject.class));
|
||||
|
||||
mt = MethodType.methodType(List.class, Object[].class);
|
||||
TO_LIST = lookup.findStatic(StringTemplateImplFactory.class, "toList", mt);
|
||||
} catch(ReflectiveOperationException ex) {
|
||||
throw new AssertionError("carrier static init fail", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link StringTemplateImpl} constructor.
|
||||
*
|
||||
* @param fragments string template fragments
|
||||
* @param type values types with StringTemplate return
|
||||
*
|
||||
* @return {@link MethodHandle} that can construct a {@link StringTemplateImpl} with arguments
|
||||
* used as values.
|
||||
*/
|
||||
static MethodHandle createStringTemplateImplMH(List<String> fragments, MethodType type) {
|
||||
Carriers.CarrierElements elements = Carriers.CarrierFactory.of(type);
|
||||
MethodHandle[] components = elements
|
||||
.components()
|
||||
.stream()
|
||||
.map(c -> c.asType(c.type().changeParameterType(0, StringTemplateImpl.class)))
|
||||
.toArray(MethodHandle[]::new);
|
||||
Class<?>[] ptypes = elements
|
||||
.components()
|
||||
.stream()
|
||||
.map(c -> c.type().returnType())
|
||||
.toArray(Class<?>[]::new);
|
||||
int[] permute = new int[ptypes.length];
|
||||
|
||||
MethodHandle interpolateMH;
|
||||
MethodType mt;
|
||||
try {
|
||||
interpolateMH = StringConcatFactory.makeConcatWithTemplate(fragments, List.of(ptypes));
|
||||
} catch (StringConcatException ex) {
|
||||
throw new RuntimeException("constructing internal string template", ex);
|
||||
}
|
||||
interpolateMH = MethodHandles.filterArguments(interpolateMH, 0, components);
|
||||
interpolateMH = MethodHandles.permuteArguments(interpolateMH, MT_STRING_STIMPL, permute);
|
||||
|
||||
mt = MethodType.methodType(List.class, ptypes);
|
||||
MethodHandle valuesMH = TO_LIST.asCollector(Object[].class, components.length).asType(mt);
|
||||
valuesMH = MethodHandles.filterArguments(valuesMH, 0, components);
|
||||
valuesMH = MethodHandles.permuteArguments(valuesMH, MT_LIST_STIMPL, permute);
|
||||
|
||||
MethodHandle constructor = MethodHandles.insertArguments(CONSTRUCTOR, 0,
|
||||
elements.primitiveCount(), elements.objectCount(),
|
||||
fragments, valuesMH, interpolateMH);
|
||||
constructor = MethodHandles.foldArguments(elements.initializer(), 0, constructor);
|
||||
|
||||
mt = MethodType.methodType(StringTemplate.class, ptypes);
|
||||
constructor = constructor.asType(mt);
|
||||
|
||||
return constructor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic {@link StringTemplate}.
|
||||
*
|
||||
* @param fragments immutable list of string fragments from string template
|
||||
* @param values immutable list of expression values
|
||||
*/
|
||||
private record SimpleStringTemplate(List<String> fragments, List<Object> values)
|
||||
implements StringTemplate {
|
||||
@Override
|
||||
public String toString() {
|
||||
return StringTemplate.toString(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new StringTemplate composed from fragments and values.
|
||||
*
|
||||
* @param fragments array of string fragments
|
||||
* @param values array of expression values
|
||||
*
|
||||
* @return StringTemplate composed from fragments and values
|
||||
*/
|
||||
static StringTemplate newTrustedStringTemplate(String[] fragments, Object[] values) {
|
||||
return new SimpleStringTemplate(List.of(fragments), toList(values));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new StringTemplate composed from fragments and values.
|
||||
*
|
||||
* @param fragments list of string fragments
|
||||
* @param values array of expression values
|
||||
*
|
||||
* @return StringTemplate composed from fragments and values
|
||||
*/
|
||||
static StringTemplate newTrustedStringTemplate(List<String> fragments, Object[] values) {
|
||||
return new SimpleStringTemplate(List.copyOf(fragments), toList(values));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new StringTemplate composed from fragments and values.
|
||||
*
|
||||
* @param fragments list of string fragments
|
||||
* @param values list of expression values
|
||||
*
|
||||
* @return StringTemplate composed from fragments and values
|
||||
*/
|
||||
|
||||
static StringTemplate newStringTemplate(List<String> fragments, List<?> values) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object> copy = (List<Object>)values.stream().toList();
|
||||
return new SimpleStringTemplate(List.copyOf(fragments), copy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect nullable elements from an array into a unmodifiable list.
|
||||
* Elements are guaranteed to be safe.
|
||||
*
|
||||
* @param elements elements to place in list
|
||||
*
|
||||
* @return unmodifiable list.
|
||||
*/
|
||||
private static List<Object> toList(Object[] elements) {
|
||||
return Arrays.stream(elements).toList();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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 General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package java.lang.runtime;
|
||||
|
||||
import java.lang.invoke.CallSite;
|
||||
import java.lang.invoke.ConstantCallSite;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.StringTemplate.Processor;
|
||||
import java.lang.StringTemplate.Processor.Linkage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.internal.access.JavaTemplateAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.javac.PreviewFeature;
|
||||
|
||||
/**
|
||||
* Manages string template bootstrap methods. These methods may be used, for example,
|
||||
* by Java compiler implementations to create {@link StringTemplate} instances. For example,
|
||||
* the java compiler will translate the following code;
|
||||
* {@snippet :
|
||||
* int x = 10;
|
||||
* int y = 20;
|
||||
* StringTemplate st = RAW."\{x} + \{y} = \{x + y}";
|
||||
* }
|
||||
* to byte code that invokes the {@link java.lang.runtime.TemplateRuntime#newStringTemplate}
|
||||
* bootstrap method to construct a {@link CallSite} that accepts two integers and produces a new
|
||||
* {@link StringTemplate} instance.
|
||||
* {@snippet :
|
||||
* MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
* MethodType mt = MethodType.methodType(StringTemplate.class, int.class, int.class);
|
||||
* CallSite cs = TemplateRuntime.newStringTemplate(lookup, "", mt, "", " + ", " = ", "");
|
||||
* ...
|
||||
* int x = 10;
|
||||
* int y = 20;
|
||||
* StringTemplate st = (StringTemplate)cs.getTarget().invokeExact(x, y);
|
||||
* }
|
||||
* If the string template requires more than
|
||||
* {@link java.lang.invoke.StringConcatFactory#MAX_INDY_CONCAT_ARG_SLOTS} value slots,
|
||||
* then the java compiler will use the
|
||||
* {@link java.lang.runtime.TemplateRuntime#newLargeStringTemplate} bootstrap method
|
||||
* instead. For example, the java compiler will translate the following code;
|
||||
* {@snippet :
|
||||
* int[] a = new int[1000], b = new int[1000];
|
||||
* ...
|
||||
* StringTemplate st = """
|
||||
* \{a[0]} - \{b[0]}
|
||||
* \{a[1]} - \{b[1]}
|
||||
* ...
|
||||
* \{a[999]} - \{b[999]}
|
||||
* """;
|
||||
* }
|
||||
* to byte code that invokes the {@link java.lang.runtime.TemplateRuntime#newLargeStringTemplate}
|
||||
* bootstrap method to construct a {@link CallSite} that accepts an array of integers and produces a new
|
||||
* {@link StringTemplate} instance.
|
||||
* {@snippet :
|
||||
* MethodType mt = MethodType.methodType(StringTemplate.class, String[].class, Object[].class);
|
||||
* CallSite cs = TemplateRuntime.newStringTemplate(lookup, "", mt);
|
||||
* ...
|
||||
* int[] a = new int[1000], b = new int[1000];
|
||||
* ...
|
||||
* StringTemplate st = (StringTemplate)cs.getTarget().invokeExact(
|
||||
* new String[] { "", " - ", "\n", " - ", "\n", ... " - ", "\n" },
|
||||
* new Object[] { a[0], b[0], a[1], b[1], ..., a[999], b[999]}
|
||||
* );
|
||||
* }
|
||||
*
|
||||
* @since 21
|
||||
*/
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.STRING_TEMPLATES)
|
||||
public final class TemplateRuntime {
|
||||
private static final JavaTemplateAccess JTA = SharedSecrets.getJavaTemplateAccess();
|
||||
|
||||
/**
|
||||
* {@link MethodHandle} to {@link TemplateRuntime#defaultProcess}.
|
||||
*/
|
||||
private static final MethodHandle DEFAULT_PROCESS_MH;
|
||||
|
||||
/**
|
||||
* {@link MethodHandle} to {@link TemplateRuntime#newTrustedStringTemplate}.
|
||||
*/
|
||||
private static final MethodHandle NEW_TRUSTED_STRING_TEMPLATE;
|
||||
|
||||
/**
|
||||
* Initialize {@link MethodHandle MethodHandles}.
|
||||
*/
|
||||
static {
|
||||
try {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
|
||||
MethodType mt = MethodType.methodType(Object.class,
|
||||
List.class, Processor.class, Object[].class);
|
||||
DEFAULT_PROCESS_MH =
|
||||
lookup.findStatic(TemplateRuntime.class, "defaultProcess", mt);
|
||||
|
||||
mt = MethodType.methodType(StringTemplate.class, String[].class, Object[].class);
|
||||
NEW_TRUSTED_STRING_TEMPLATE =
|
||||
lookup.findStatic(StringTemplateImplFactory.class, "newTrustedStringTemplate", mt);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new AssertionError("string bootstrap fail", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor.
|
||||
*/
|
||||
private TemplateRuntime() {
|
||||
throw new AssertionError("private constructor");
|
||||
}
|
||||
|
||||
/**
|
||||
* String template bootstrap method for creating string templates.
|
||||
* The static arguments include the fragments list.
|
||||
* The non-static arguments are the values.
|
||||
*
|
||||
* @param lookup method lookup from call site
|
||||
* @param name method name - not used
|
||||
* @param type method type
|
||||
* (ptypes...) -> StringTemplate
|
||||
* @param fragments fragment array for string template
|
||||
*
|
||||
* @return {@link CallSite} to handle create string template
|
||||
*
|
||||
* @throws NullPointerException if any of the arguments is null
|
||||
* @throws Throwable if linkage fails
|
||||
*/
|
||||
public static CallSite newStringTemplate(MethodHandles.Lookup lookup,
|
||||
String name,
|
||||
MethodType type,
|
||||
String... fragments) throws Throwable {
|
||||
Objects.requireNonNull(lookup, "lookup is null");
|
||||
Objects.requireNonNull(name, "name is null");
|
||||
Objects.requireNonNull(type, "type is null");
|
||||
Objects.requireNonNull(fragments, "fragments is null");
|
||||
|
||||
MethodHandle mh = StringTemplateImplFactory
|
||||
.createStringTemplateImplMH(List.of(fragments), type).asType(type);
|
||||
|
||||
return new ConstantCallSite(mh);
|
||||
}
|
||||
|
||||
/**
|
||||
* String template bootstrap method for creating large string templates,
|
||||
* i.e., when the number of value slots exceeds
|
||||
* {@link java.lang.invoke.StringConcatFactory#MAX_INDY_CONCAT_ARG_SLOTS}.
|
||||
* The non-static arguments are the fragments array and values array.
|
||||
*
|
||||
* @param lookup method lookup from call site
|
||||
* @param name method name - not used
|
||||
* @param type method type
|
||||
* (String[], Object[]) -> StringTemplate
|
||||
*
|
||||
* @return {@link CallSite} to handle create large string template
|
||||
*
|
||||
* @throws NullPointerException if any of the arguments is null
|
||||
* @throws Throwable if linkage fails
|
||||
*/
|
||||
public static CallSite newLargeStringTemplate(MethodHandles.Lookup lookup,
|
||||
String name,
|
||||
MethodType type) throws Throwable {
|
||||
Objects.requireNonNull(lookup, "lookup is null");
|
||||
Objects.requireNonNull(name, "name is null");
|
||||
Objects.requireNonNull(type, "type is null");
|
||||
|
||||
return new ConstantCallSite(NEW_TRUSTED_STRING_TEMPLATE.asType(type));
|
||||
}
|
||||
|
||||
/**
|
||||
* String template bootstrap method for static final processors.
|
||||
* The static arguments include the fragments array and a {@link MethodHandle}
|
||||
* to retrieve the value of the static final processor.
|
||||
* The non-static arguments are the values.
|
||||
*
|
||||
* @param lookup method lookup from call site
|
||||
* @param name method name - not used
|
||||
* @param type method type
|
||||
* (ptypes...) -> Object
|
||||
* @param processorGetter {@link MethodHandle} to get static final processor
|
||||
* @param fragments fragments from string template
|
||||
*
|
||||
* @return {@link CallSite} to handle string template processing
|
||||
*
|
||||
* @throws NullPointerException if any of the arguments is null
|
||||
* @throws Throwable if linkage fails
|
||||
*
|
||||
* @implNote this method is likely to be revamped before exiting preview.
|
||||
*/
|
||||
public static CallSite processStringTemplate(MethodHandles.Lookup lookup,
|
||||
String name,
|
||||
MethodType type,
|
||||
MethodHandle processorGetter,
|
||||
String... fragments) throws Throwable {
|
||||
Objects.requireNonNull(lookup, "lookup is null");
|
||||
Objects.requireNonNull(name, "name is null");
|
||||
Objects.requireNonNull(type, "type is null");
|
||||
Objects.requireNonNull(processorGetter, "processorGetter is null");
|
||||
Objects.requireNonNull(fragments, "fragments is null");
|
||||
|
||||
Processor<?, ?> processor = (Processor<?, ?>)processorGetter.invoke();
|
||||
MethodHandle mh = processor instanceof Linkage linkage
|
||||
? linkage.linkage(List.of(fragments), type)
|
||||
: defaultProcessMethodHandle(type, processor, List.of(fragments));
|
||||
|
||||
return new ConstantCallSite(mh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a simple {@link StringTemplate} and then invokes the processor's process method.
|
||||
*
|
||||
* @param fragments fragments from string template
|
||||
* @param processor {@link Processor} to process
|
||||
* @param values array of expression values
|
||||
*
|
||||
* @return result of processing the string template
|
||||
*
|
||||
* @throws Throwable when {@link Processor#process(StringTemplate)} throws
|
||||
*/
|
||||
private static Object defaultProcess(
|
||||
List<String> fragments,
|
||||
Processor<?, ?> processor,
|
||||
Object[] values
|
||||
) throws Throwable {
|
||||
return processor.process(StringTemplate.of(fragments, Arrays.stream(values).toList()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a {@link MethodHandle} which is effectively invokes
|
||||
* {@code processor.process(new StringTemplate(fragments, values...)}.
|
||||
*
|
||||
* @return default process {@link MethodHandle}
|
||||
*/
|
||||
private static MethodHandle defaultProcessMethodHandle(
|
||||
MethodType type,
|
||||
Processor<?, ?> processor,
|
||||
List<String> fragments
|
||||
) {
|
||||
MethodHandle mh = MethodHandles.insertArguments(DEFAULT_PROCESS_MH, 0, fragments, processor);
|
||||
return mh.asCollector(Object[].class, type.parameterCount()).asType(type);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code 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 General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package java.lang.runtime;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.internal.access.JavaLangAccess;
|
||||
import jdk.internal.access.JavaTemplateAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
|
||||
/**
|
||||
* This class provides runtime support for string templates. The methods within
|
||||
* are intended for internal use only.
|
||||
*
|
||||
* @since 21
|
||||
*
|
||||
* Warning: This class is part of PreviewFeature.Feature.STRING_TEMPLATES.
|
||||
* Do not rely on its availability.
|
||||
*/
|
||||
final class TemplateSupport implements JavaTemplateAccess {
|
||||
|
||||
/**
|
||||
* Private constructor.
|
||||
*/
|
||||
private TemplateSupport() {
|
||||
}
|
||||
|
||||
static {
|
||||
SharedSecrets.setJavaTemplateAccess(new TemplateSupport());
|
||||
}
|
||||
|
||||
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
|
||||
|
||||
/**
|
||||
* Returns a StringTemplate composed from fragments and values.
|
||||
*
|
||||
* @implSpec The {@code fragments} list size must be one more that the
|
||||
* {@code values} list size.
|
||||
*
|
||||
* @param fragments list of string fragments
|
||||
* @param values list of expression values
|
||||
*
|
||||
* @return StringTemplate composed from fragments and values
|
||||
*
|
||||
* @throws IllegalArgumentException if fragments list size is not one more
|
||||
* than values list size
|
||||
* @throws NullPointerException if fragments is null or values is null or if any fragment is null.
|
||||
*
|
||||
* @implNote Contents of both lists are copied to construct immutable lists.
|
||||
*/
|
||||
@Override
|
||||
public StringTemplate of(List<String> fragments, List<?> values) {
|
||||
return StringTemplateImplFactory.newStringTemplate(fragments, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string that interleaves the elements of values between the
|
||||
* elements of fragments.
|
||||
*
|
||||
* @param fragments list of String fragments
|
||||
* @param values list of expression values
|
||||
*
|
||||
* @return String interpolation of fragments and values
|
||||
*/
|
||||
@Override
|
||||
public String interpolate(List<String> fragments, List<?> values) {
|
||||
int fragmentsSize = fragments.size();
|
||||
int valuesSize = values.size();
|
||||
if (fragmentsSize == 1) {
|
||||
return fragments.get(0);
|
||||
}
|
||||
int size = fragmentsSize + valuesSize;
|
||||
String[] strings = new String[size];
|
||||
int i = 0, j = 0;
|
||||
for (; j < valuesSize; j++) {
|
||||
strings[i++] = fragments.get(j);
|
||||
strings[i++] = String.valueOf(values.get(j));
|
||||
}
|
||||
strings[i] = fragments.get(j);
|
||||
return JLA.join("", "", "", strings, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine one or more {@link StringTemplate StringTemplates} to produce a combined {@link StringTemplate}.
|
||||
* {@snippet :
|
||||
* StringTemplate st = StringTemplate.combine("\{a}", "\{b}", "\{c}");
|
||||
* assert st.interpolate().equals("\{a}\{b}\{c}");
|
||||
* }
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
@Override
|
||||
public StringTemplate combine(StringTemplate... sts) {
|
||||
Objects.requireNonNull(sts, "sts must not be null");
|
||||
if (sts.length == 0) {
|
||||
return StringTemplate.of("");
|
||||
} else if (sts.length == 1) {
|
||||
return Objects.requireNonNull(sts[0], "string templates should not be null");
|
||||
}
|
||||
int size = 0;
|
||||
for (StringTemplate st : sts) {
|
||||
Objects.requireNonNull(st, "string templates should not be null");
|
||||
size += st.values().size();
|
||||
}
|
||||
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 StringTemplateImplFactory.newTrustedStringTemplate(combinedFragments, combinedValues);
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue