org.graalvm.polyglot.Value Maven / Gradle / Ivy
Show all versions of graal-sdk Show documentation
/*
* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.graalvm.polyglot;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractValueImpl;
import org.graalvm.polyglot.proxy.Proxy;
/**
* Represents a polyglot value that can be accessed using a set of language agnostic operations.
* Polyglot values represent values from {@link #isHostObject() host} or guest language. Polyglot
* values are bound to a {@link Context context}. If the context is closed then all value operations
* throw an {@link IllegalStateException}.
*
* Polyglot values have one of the following types:
*
* - {@link #isNull() Null}: This value represents a
null
like value. Certain
* languages might use a different name or use multiple values to represent null
like
* values.
* - {@link #isNumber() Number}: This value represents a floating or fixed point number. The
* number value may be accessed as {@link #asByte() byte}, {@link #asShort() short} {@link #asInt()
* int} {@link #asLong() long}, {@link #asFloat() float} or {@link #asDouble() double} value.
*
- {@link #isBoolean() Boolean}. This value represents a boolean value. The boolean value can be
* accessed using {@link #asBoolean()}.
*
- {@link #isString() String}: This value represents a string value. The string value can be
* accessed using {@link #asString()}.
*
- {@link #isHostObject() Host Object}: This value represents a value of the host language
* (Java). The original Java value can be accessed using {@link #asHostObject()}.
*
- {@link #isProxyObject() Proxy Object}: This value represents a {@link Proxy proxy} value.
*
- {@link #isNativePointer() Native Pointer}: This value represents a native pointer. The native
* pointer value can be accessed using {@link #asNativePointer()}.
*
* In addition any value may have one or more of the following traits:
*
* - {@link #hasArrayElements() Array Elements}: This value may contain array elements. The array
* indices always start with
0
, also if the language uses a different style.
* - {@link #hasMembers() Members}: This value may contain members. Members are structural
* elements of an object. For example, the members of a Java object are all public methods and
* fields. Members are accessible using {@link #getMember(String)}.
*
- {@link #canExecute() Executable}: This value can be {@link #execute(Object...) executed}.
* This indicates that the value represents that can be executed. Guest language examples for
* executable elements are functions, methods, closures or promises.
*
- {@link #canInstantiate() Instantiable}: This value can be {@link #newInstance(Object...)
* instantiated}. For example, Java classes are instantiable.
*
*
* In addition to the language agnostic types, the language specific type can be accessed using
* {@link #getMetaObject()}. The identity of value objects is unspecified and should not be relied
* upon. For example, multiple calls to {@link #getArrayElement(long)} with the same index might
* return the same or different instances of {@link Value}. The {@link #equals(Object) equality} of
* values is based on the identity of the value instance. All values return a human-readable
* {@link #toString() string} for debugging, formatted by the original language.
*
* Polyglot values may be converted to host objects using {@link #as(Class)}. In addition values may
* be created form Java values using {@link Context#asValue(Object)}.
*
* @see Context
* @see Engine
* @see PolyglotException
* @since 1.0
*/
public final class Value {
final Object receiver;
final AbstractValueImpl impl;
Value(AbstractValueImpl impl, Object value) {
this.impl = impl;
this.receiver = value;
}
/**
* Returns the meta representation of this polyglot value. The interpretation of this function
* differs for each guest language. A language agnostic way to get to a type name is:
* value.{@link #getMetaObject() getMetaObject()}.{@link #toString() toString()}
. If a
* language does not provide any meta object information, null
is returned.
*
* @throws IllegalStateException if the context is already closed.
* @since 1.0
*/
public Value getMetaObject() {
return impl.getMetaObject(receiver);
}
/**
* Returns true
if this polyglot value has array elements. In this case array
* elements can be accessed using {@link #getArrayElement(long)},
* {@link #setArrayElement(long, Object)}, {@link #removeArrayElement(long)} and the array size
* can be queried using {@link #getArraySize()}.
*
* @throws IllegalStateException if the context is already closed.
* @throws PolyglotException if a guest language error occurred during execution.
* @since 1.0
*/
public boolean hasArrayElements() {
return impl.hasArrayElements(receiver);
}
/**
* Returns the array element of a given index. Polyglot arrays start with index 0
,
* independent of the guest language. The given array index must be greater or equal 0.
*
* @throws ArrayIndexOutOfBoundsException if the array index does not exist.
* @throws UnsupportedOperationException if the value does not have any
* {@link #hasArrayElements() array elements} or if the index exists but is not
* readable.
* @throws IllegalStateException if the context is already closed.
* @throws PolyglotException if a guest language error occurred during execution.
* @since 1.0
*/
public Value getArrayElement(long index) {
return impl.getArrayElement(receiver, index);
}
/**
* Sets the value at a given index. Polyglot array start with index 0
, independent
* of the guest language. The array element value is subject to polyglot value mapping rules as
* described in {@link Context#asValue(Object)}.
*
* @throws ArrayIndexOutOfBoundsException if the array index does not exist.
* @throws UnsupportedOperationException if the value does not have any
* {@link #hasArrayElements() array elements} or if the index exists but is not
* modifiable.
* @throws IllegalStateException if the context is already closed.
* @throws PolyglotException if a guest language error occurred during execution.
* @since 1.0
*/
public void setArrayElement(long index, Object value) {
impl.setArrayElement(receiver, index, value);
}
/**
* Removes an array element at a given index. Returns true
if the underlying array
* element could be removed, otherwise false
.
*
* @throws ArrayIndexOutOfBoundsException if the array index does not exist.
* @throws UnsupportedOperationException if the value does not have any
* {@link #hasArrayElements() array elements} or if the index exists but is not
* removable.
* @throws IllegalStateException if the context is already closed.
* @throws PolyglotException if a guest language error occurred during execution.
* @since 1.0
*/
public boolean removeArrayElement(long index) {
return impl.removeArrayElement(receiver, index);
}
/**
* Returns the array size for values with array elements.
*
* @throws UnsupportedOperationException if the value does not have any
* {@link #hasArrayElements() array elements}.
* @throws IllegalStateException if the context is already closed.
* @throws PolyglotException if a guest language error occurred during execution.
* @since 1.0
*/
public long getArraySize() {
return impl.getArraySize(receiver);
}
/**
* Returns true
if this value generally supports containing members. To check
* whether a value has no members use
* {@link #getMemberKeys() getMemberKeys()}.{@link Set#isEmpty() isEmpty()}
* instead. If polyglot value has members, it may also support {@link #getMember(String)},
* {@link #putMember(String, Object)} and {@link #removeMember(String)}.
*
* @see #hasMember(String) To check the existence of members.
* @see #getMember(String) To read members.
* @see #putMember(String, Object) To write members.
* @see #removeMember(String) To remove a member.
* @see #getMemberKeys() For a list of members.
* @throws IllegalStateException if the context is already closed.
* @throws PolyglotException if a guest language error occurred during execution.
* @since 1.0
*/
public boolean hasMembers() {
return impl.hasMembers(receiver);
}
/**
* Returns true
if such a member exists for a given identifier
. If the
* value has no {@link #hasMembers() members} then {@link #hasMember(String)} returns
* false
.
*
* @throws PolyglotException if a guest language error occurred during execution.
* @throws NullPointerException if the identifier is null.
* @since 1.0
*/
public boolean hasMember(String identifier) {
Objects.requireNonNull(identifier, "identifier");
return impl.hasMember(receiver, identifier);
}
/**
* Returns the member with a given identifier
or null
if the member
* does not exist.
*
* @throws UnsupportedOperationException if the value {@link #hasMembers() has no members} or
* the given identifier exists but is not readable.
* @throws PolyglotException if a guest language error occurred during execution.
* @throws NullPointerException if the identifier is null.
* @since 1.0
*/
public Value getMember(String identifier) {
Objects.requireNonNull(identifier, "identifier");
return impl.getMember(receiver, identifier);
}
/**
* Returns a set of all member keys. Calling {@link Set#contains(Object)} with a string key is
* equivalent of calling {@link #hasMember(String)}. Removing an element from the returned set
* is equivalent to calling {@link #removeMember(String)}. Adding an element to the set is
* equivalent to calling {@linkplain #putMember(String, Object) putMember(key, null)}. If the
* value does not support {@link #hasMembers() members} then an empty unmodifiable set is
* returned. If the context gets closed while the returned set is still alive, then the set will
* throw an {@link IllegalStateException} if any method except Object methods is invoked.
*
* @throws IllegalStateException if the context is already {@link Context#close() closed}.
* @throws PolyglotException if a guest language error occurred during execution.
* @since 1.0
*/
public Set getMemberKeys() {
return impl.getMemberKeys(receiver);
}
/**
* Sets the value of a member using an identifier. The member value is subject to polyglot value
* mapping rules as described in {@link Context#asValue(Object)}.
*
* @throws IllegalStateException if the context is already {@link Context#close() closed}.
* @throws UnsupportedOperationException if the value does not have any {@link #hasMembers()
* members}, the key does not exist and new members cannot be added, or the existing
* member is not modifiable.
* @throws PolyglotException if a guest language error occurred during execution.
* @throws NullPointerException if the identifier is null.
* @since 1.0
*/
public void putMember(String identifier, Object value) {
Objects.requireNonNull(identifier, "identifier");
impl.putMember(receiver, identifier, value);
}
/**
* Removes a single member from the object. Returns true
if the member was
* successfully removed, false
if such a member does not exist.
*
* @throws UnsupportedOperationException if the value does not have any {@link #hasMembers()
* members} or if the key {@link #hasMember(String) exists} but cannot be removed.
* @throws IllegalStateException if the context is already {@link Context#close() closed}.
* @throws PolyglotException if a guest language error occurred during execution.
* @throws NullPointerException if the identifier is null.
* @since 1.0
*/
public boolean removeMember(String identifier) {
Objects.requireNonNull(identifier, "identifier");
return impl.removeMember(receiver, identifier);
}
// executable
/**
* Returns true
if the value can be {@link #execute(Object...) executed}.
*
* @throws IllegalStateException if the underlying context was closed.
* @see #execute(Object...)
* @since 1.0
*/
public boolean canExecute() {
return impl.canExecute(receiver);
}
/**
* Executes this value if it {@link #canExecute() can} be executed and returns its result. If no
* result value is expected or needed use {@link #executeVoid(Object...)} for better
* performance. All arguments are subject to polyglot value mapping rules as described in
* {@link Context#asValue(Object)}.
*
* @throws IllegalStateException if the underlying context was closed.
* @throws IllegalArgumentException if a wrong number of arguments was provided or one of the
* arguments was not applicable.
* @throws UnsupportedOperationException if this value cannot be executed.
* @throws PolyglotException if a guest language error occurred during execution.
* @throws NullPointerException if the arguments array is null.
* @see #executeVoid(Object...)
* @since 1.0
*/
public Value execute(Object... arguments) {
if (arguments.length == 0) {
// specialized entry point for zero argument execute calls
return impl.execute(receiver);
} else {
return impl.execute(receiver, arguments);
}
}
/**
* Executes this value if it {@link #canExecute() can} be executed. All arguments are subject to
* polyglot value mapping rules as described in {@link Context#asValue(Object)}.
*
* @throws IllegalStateException if the underlying context was closed.
* @throws IllegalArgumentException if a wrong number of arguments was provided or one of the
* arguments was not applicable.
* @throws UnsupportedOperationException if this value cannot be executed.
* @throws PolyglotException if a guest language error occurred during execution.
* @throws NullPointerException if the arguments array is null.
* @see #execute(Object...)
* @since 1.0
*/
public void executeVoid(Object... arguments) {
if (arguments.length == 0) {
// specialized entry point for zero argument execute calls
impl.executeVoid(receiver);
} else {
impl.executeVoid(receiver, arguments);
}
}
/**
* Returns true
if the value can be instantiated. This indicates that the
* {@link #newInstance(Object...)} can be used with this value.
*
* @since 1.0
*/
public boolean canInstantiate() {
return impl.canInstantiate(receiver);
}
/**
* Instantiates this value if it {@link #canInstantiate() can} be instantiated. All arguments
* are subject to polyglot value mapping rules as described in {@link Context#asValue(Object)}.
*
* @throws IllegalStateException if the underlying context was closed.
* @throws IllegalArgumentException if a wrong number of arguments was provided or one of the
* arguments was not applicable.
* @throws UnsupportedOperationException if this value cannot be instantiated.
* @throws PolyglotException if a guest language error occurred during execution.
* @throws NullPointerException if the arguments array is null.
* @since 1.0
*/
public Value newInstance(Object... arguments) {
Objects.requireNonNull(arguments, "arguments");
return impl.newInstance(receiver, arguments);
}
/**
* Returns true
if this value represents a string.
*
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @since 1.0
*/
public boolean isString() {
return impl.isString(receiver);
}
/**
* Returns the {@link String} value if this value {@link #isString() is} a string. This method
* returns null
if this value represents a {@link #isNull() null} value.
*
* @throws ClassCastException if this value could not be converted to string.
* @throws UnsupportedOperationException if this value does not represent a string.
* @throws PolyglotException if a guest language error occurred during execution.
* @since 1.0
*/
public String asString() {
return impl.asString(receiver);
}
/**
* Returns true
if this value represents a {@link #isNumber() number} and the value
* fits in int
, else false
.
*
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @see #asInt()
* @since 1.0
*/
public boolean fitsInInt() {
return impl.fitsInInt(receiver);
}
/**
* Returns an int
representation if this value if it is {@link #isNumber() number}
* and the value {@link #fitsInInt() fits}.
*
* @throws NullPointerException if this value represents {@link #isNull() null}.
* @throws ClassCastException if this value could not be converted.
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @since 1.0
*/
public int asInt() {
return impl.asInt(receiver);
}
/**
* Returns true
if this value represents a boolean value.
*
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @see #asBoolean()
* @since 1.0
*/
public boolean isBoolean() {
return impl.isBoolean(receiver);
}
/**
* Returns an boolean
representation if this value if it is {@link #isBoolean()
* boolean}.
*
* @throws NullPointerException if this value represents {@link #isNull() null}
* @throws ClassCastException if this value could not be converted.
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @since 1.0
*/
public boolean asBoolean() {
return impl.asBoolean(receiver);
}
/**
* Returns true
if this value represents a {@link #isNumber() number}, else
* false
. The number value may be accessed as {@link #asByte() byte},
* {@link #asShort() short} {@link #asInt() int} {@link #asLong() long}, {@link #asFloat()
* float} or {@link #asDouble() double} value.
*
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @since 1.0
*/
public boolean isNumber() {
return impl.isNumber(receiver);
}
/**
* Returns true
if this value represents a {@link #isNumber() number} and the value
* fits in long
, else false
.
*
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @see #asLong()
* @since 1.0
*/
public boolean fitsInLong() {
return impl.fitsInLong(receiver);
}
/**
* Returns an long
representation if this value if it is {@link #isNumber() number}
* and the value {@link #fitsInLong() fits}.
*
* @throws NullPointerException if this value represents {@link #isNull() null}.
* @throws ClassCastException if this value could not be converted to long.
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @since 1.0
*/
public long asLong() {
return impl.asLong(receiver);
}
/**
* Returns true
if this value represents a {@link #isNumber() number} and the value
* fits in double
, else false
.
*
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @see #asDouble()
* @since 1.0
*/
public boolean fitsInDouble() {
return impl.fitsInDouble(receiver);
}
/**
* Returns an double
representation if this value if it is {@link #isNumber()
* number} and the value {@link #fitsInDouble() fits}.
*
* @throws NullPointerException if this value represents {@link #isNull() null}.
* @throws ClassCastException if this value could not be converted.
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @since 1.0
*/
public double asDouble() {
return impl.asDouble(receiver);
}
/**
* Returns true
if this value represents a {@link #isNumber() number} and the value
* fits in float
, else false
.
*
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @see #asFloat()
* @since 1.0
*/
public boolean fitsInFloat() {
return impl.fitsInFloat(receiver);
}
/**
* Returns an float
representation if this value if it is {@link #isNumber()
* number} and the value {@link #fitsInFloat() fits}.
*
* @throws NullPointerException if this value represents {@link #isNull() null}.
* @throws ClassCastException if this value could not be converted.
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @since 1.0
*/
public float asFloat() {
return impl.asFloat(receiver);
}
/**
* Returns true
if this value represents a {@link #isNumber() number} and the value
* fits in byte
, else false
.
*
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @see #asByte()
* @since 1.0
*/
public boolean fitsInByte() {
return impl.fitsInByte(receiver);
}
/**
* Returns an byte
representation if this value if it is {@link #isNumber() number}
* and the value {@link #fitsInByte() fits}.
*
* @throws NullPointerException if this value represents {@link #isNull() null}.
* @throws ClassCastException if this value could not be converted.
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @since 1.0
*/
public byte asByte() {
return impl.asByte(receiver);
}
/**
* Returns true
if this value represents a {@link #isNumber() number} and the value
* fits in short
, else false
.
*
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @see #asShort()
* @since 1.0
*/
public boolean fitsInShort() {
return impl.fitsInShort(receiver);
}
/**
* Returns an short
representation if this value if it is {@link #isNumber()
* number} and the value {@link #fitsInShort() fits}.
*
* @throws NullPointerException if this value represents {@link #isNull() null}.
* @throws ClassCastException if this value could not be converted.
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @since 1.0
*/
public short asShort() {
return impl.asShort(receiver);
}
/**
* Returns true
if this value is a null
like.
*
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @since 1.0
*/
public boolean isNull() {
return impl.isNull(receiver);
}
/**
* Returns true
if this value is a native pointer. The value of the pointer can be
* accessed using {@link #asNativePointer()}.
*
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @since 1.0
*/
public boolean isNativePointer() {
return impl.isNativePointer(receiver);
}
/**
* Returns the value of the pointer as long
value.
*
* @throws UnsupportedOperationException if the value is not a pointer.
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @since 1.0
*/
public long asNativePointer() {
return impl.asNativePointer(receiver);
}
/**
* Returns true
if the value originated form the host language Java. In such a case
* the value can be accessed using {@link #asHostObject()}.
*
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @since 1.0
*/
public boolean isHostObject() {
return impl.isHostObject(receiver);
}
/**
* Returns the original Java host language object.
*
* @throws UnsupportedOperationException if {@link #isHostObject()} is false
.
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @since 1.0
*/
@SuppressWarnings("unchecked")
public T asHostObject() {
return (T) impl.asHostObject(receiver);
}
/**
* Returns true
whether this value represents a {@link Proxy}. The proxy instance
* can be unboxed using {@link #asProxyObject()}.
*
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @since 1.0
*/
public boolean isProxyObject() {
return impl.isProxyObject(receiver);
}
/**
* Returns the unboxed instance of the {@link Proxy}. Proxies are not automatically boxed to
* {@link #isHostObject() host objects} on host language call boundaries (Java methods).
*
* @throws UnsupportedOperationException if a value is not a proxy object.
* @throws PolyglotException if a guest language error occurred during execution.
* @throws IllegalStateException if the underlying context was closed.
* @since 1.0
*/
@SuppressWarnings("unchecked")
public T asProxyObject() {
return (T) impl.asProxyObject(receiver);
}
/**
* Maps a polyglot value to a value with a given Java target type.
*
* Target type mapping
*
* The following target types are supported and interpreted in the following order:
*
* {@link Value}.class
is always supported and returns this instance.
* - If the value represents a {@link #isHostObject() host object} then all classes
* implemented or extended by the host object can be used as target type.
*
{@link String}.class
is supported if the value is a {@link #isString()
* string}.
* {@link Character}.class
is supported if the value is a {@link #isString()
* string} of length one.
* {@link Number}.class
is supported if the value is a {@link #isNumber()
* number}. {@link Byte}, {@link Short}, {@link Integer}, {@link Long}, {@link Float} and
* {@link Double} are allowed if they fit without conversion. If a conversion is necessary then
* a {@link ClassCastException} is thrown. Primitive class literals throw a
* {@link NullPointerException} if the value represents {@link #isNull() null}.
* {@link Boolean}.class
is supported if the value is a {@link #isBoolean()
* boolean}. Primitive {@link Boolean boolean.class} literal is also supported. The primitive
* class literal throws a {@link NullPointerException} if the value represents {@link #isNull()
* null}.
* - Any Java type in the type hierarchy of a {@link #isHostObject() host object}.
*
{@link Object}.class
is always supported. See section Object mapping rules.
* {@link Map}.class
is supported if the value has {@link #hasMembers()
* members} or {@link #hasArrayElements() array elements}. The returned map can be safely cast
* to Map
* A {@link ClassCastException} is thrown for other unsupported target types.
*
* JavaScript Usage Examples:
*
*
* Context context = Context.create();
* assert context.eval("js", "undefined").as(Object.class) == null;
* assert context.eval("js", "'foobar'").as(String.class).equals("foobar");
* assert context.eval("js", "42").as(Integer.class) == 42;
* assert context.eval("js", "{foo:'bar'}").as(Map.class).get("foo").equals("bar");
* assert context.eval("js", "[42]").as(List.class).get(0).equals(42);
* assert ((Map)context.eval("js", "[{foo:'bar'}]").as(List.class).get(0)).get("foo").equals("bar");
*
* @FunctionalInterface interface IntFunction { int foo(int value); }
* assert context.eval("js", "(function(a){a})").as(IntFunction.class).foo(42) == 42;
*
* @FunctionalInterface interface StringListFunction { int foo(List<String> value); }
* assert context.eval("js", "(function(a){a.length})").as(StringListFunction.class)
* .foo(new String[]{"42"}) == 1;
*
*
* Object target type mapping
*
* Object target mapping is useful to map polyglot values to its closest corresponding standard
* JDK type.
*
* The following rules apply when Object
is used as a target type:
*
* - If the value represents {@link #isNull() null} then
null
is returned.
* - If the value is a {@link #isHostObject() host object} then the value is coerced to
* {@link #asHostObject() host object value}.
*
- If the value is a {@link #isString() string} then the value is coerced to {@link String}
* or {@link Character}.
*
- If the value is a {@link #isBoolean() boolean} then the value is coerced to
* {@link Boolean}.
*
- If the value is a {@link #isNumber() number} then the value is coerced to {@link Number}.
* The specific sub type of the {@link Number} is not specified. Users need to be prepared for
* any Number subclass including {@link BigInteger} or {@link BigDecimal}. It is recommended to
* cast to {@link Number} and then convert to a Java primitive like with
* {@link Number#longValue()}.
*
- If the value {@link #hasMembers() has members} then the result value will implement
* {@link Map}. If this value {@link #hasMembers() has members} then all members are accessible
* using {@link String} keys. The {@link Map#size() size} of the returned {@link Map} is equal
* to the count of all members. The returned value may also implement {@link Function} if the
* value can be {@link #canExecute() executed} or {@link #canInstantiate() instantiated}.
*
- If the value has {@link #hasArrayElements() array elements} and it has an
* {@link Value#getArraySize() array size} that is smaller or equal than
* {@link Integer#MAX_VALUE} then the result value will implement {@link List}. Every array
* element of the value maps to one list element. The size of the returned list maps to the
* array size of the value. The returned value may also implement {@link Function} if the value
* can be {@link #canExecute() executed} or {@link #canInstantiate() instantiated}.
*
- If the value can be {@link #canExecute() executed} or {@link #canInstantiate()
* instantiated} then the result value implements {@link Function Function}. By default the
* argument of the function will be used as single argument to the function when executed. If a
* value of type {@link Object Object[]} is provided then the function will executed with those
* arguments. The returned function may also implement {@link Map} if the value has
* {@link #hasArrayElements() array elements} or {@link #hasMembers() members}.
*
- If none of the above rules apply then this {@link Value} instance is returned.
*
* Returned {@link #isHostObject() host objects}, {@link String}, {@link Number},
* {@link Boolean} and null
values have unlimited lifetime. Other values will throw
* an {@link IllegalStateException} for any operation if their originating {@link Context
* context} was closed.
*
* If a {@link Map} element is modified, a {@link List} element is modified or a
* {@link Function} argument is provided then these values are interpreted according to the
* {@link Context#asValue(Object) host to polyglot value mapping rules}.
*
* JavaScript Usage Examples:
*
*
* Context context = Context.create();
* assert context.eval("js", "undefined").as(Object.class) == null;
* assert context.eval("js", "'foobar'").as(Object.class) instanceof String;
* assert context.eval("js", "42").as(Object.class) instanceof Number;
* assert context.eval("js", "[]").as(Object.class) instanceof Map;
* assert context.eval("js", "{}").as(Object.class) instanceof Map;
* assert ((Map
*
* Object Identity
*
* If polyglot values are mapped as Java primitives such as {@link Boolean}, null
,
* {@link String}, {@link Character} or {@link Number}, then the identity of the polyglot value
* is not preserved. All other results can be converted back to a {@link Value polyglot value}
* using {@link Context#asValue(Object)}.
*
* Mapping Example using JavaScript: This example first creates a new JavaScript object
* and maps it to a {@link Map}. Using the {@link Context#asValue(Object)} it is possible to
* recreate the {@link Value polyglot value} from the Java map. The JavaScript object identity
* is preserved in the process.
*
*
* Context context = Context.create();
* Map
*
* @see #as(TypeLiteral) to map to generic type signatures.
* @param targetType the target Java type to map
* @throws ClassCastException if polyglot value could not be mapped to the target type.
* @throws PolyglotException if the conversion triggered a guest language error.
* @throws IllegalStateException if the underlying context is already closed.
* @throws NullPointerException if the target type is null.
* @since 1.0
*/
public T as(Class targetType) throws ClassCastException, IllegalStateException, PolyglotException {
Objects.requireNonNull(targetType, "targetType");
if (targetType == Value.class) {
return targetType.cast(this);
}
return impl.as(receiver, targetType);
}
/**
* Maps a polyglot value to a given Java target type literal. For usage instructions see
* {@link TypeLiteral}.
*
* Usage example:
*
*
* static final TypeLiteral> STRING_LIST = new TypeLiteral>() {
* };
*
* Context context = Context.create();
* List javaList = context.eval("js", "['foo', 'bar', 'bazz']").as(STRING_LIST);
* assert javaList.get(0).equals("foo");
*
*
* @throws NullPointerException if the target type is null.
* @see #as(Class)
* @since 1.0
*/
public T as(TypeLiteral targetType) {
Objects.requireNonNull(targetType, "targetType");
return impl.as(receiver, targetType);
}
/**
* A string representation of the value formatted by the original language.
*
* @since 1.0
*/
@Override
public String toString() {
return impl.toString(receiver);
}
/**
* Returns the declared source location of the value.
*
* @return the {@link SourceSection} or null if unknown
* @since 1.0
*/
public SourceSection getSourceLocation() {
return impl.getSourceLocation(receiver);
}
/**
* Converts a Java host value to a polyglot value representation using
* {@link Context#asValue(Object)} with the {@link Context#getCurrent() current} context. This
* method is a short-cut for Context.getCurrent().asValue(o)
.
*
* @param o the object to convert
* @throws IllegalStateException if no context is currently entered.
* @see Context#asValue(Object) Conversion rules.
* @see Context#getCurrent() Looking up the current context.
* @since 1.0
*/
public static Value asValue(Object o) {
return Context.getCurrent().asValue(o);
}
}