All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.graalvm.polyglot.Value Maven / Gradle / Ivy

Go to download

GraalVM is an ecosystem for compiling and running applications written in multiple languages. GraalVM removes the isolation between programming languages and enables interoperability in a shared runtime.

The newest version!
/*
 * 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. The key type in such a case is either {@link String} or {@link Long}. * It is recommended to use {@link #as(TypeLiteral) type literals} to specify the expected * collection component types. With type literals the value type can be restricted, for example * to Map. If the raw {@link Map}.class or an Object * component type is used, then the return types of the the list are subject to Object target * type mapping rules recursively. *
  • {@link List}.class is supported 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}. The returned list can be safely cast to * List<Object>. It is recommended to use {@link #as(TypeLiteral) type * literals} to specify the expected component type. With type literals the value type can be * restricted to any supported target type, for example to List<Integer>. If * the raw {@link List}.class or an Object component type is used, then the return * types of the the list are recursively subject to Object target type mapping rules. *
  • Any Java array type of a supported target type. The values of the value will be eagerly * coerced and copied into a new instance of the provided array type. This means that changes in * returned array will not be reflected in the original value. Since conversion to a Java array * might be an expensive operation it is recommended to use the `List` or `Collection` target * type if possible. *
  • Any {@link FunctionalInterface functional} interface if the value can be * {@link #canExecute() executed} or {@link #canInstantiate() instantiated}. In case a value can * be executed and instantiated then the returned implementation of the interface will be * {@link #execute(Object...) executed}. The coercion to the parameter types of functional * interface method is converted using the semantics of {@link #as(Class)}. If a standard * functional interface like {@link Function} are used, is recommended to use * {@link #as(TypeLiteral) type literals} to specify the expected generic method parameter and * return type. *
  • Any interface if the value {@link #hasMembers()}. Each method or field name maps to one * {@link #getMember(String) member} of the value. Whenever a method of the interface is * executed a member with the method or field name must exist otherwise a * {@link UnsupportedOperationException} is thrown when the method is executed. If one of the * parameters cannot be mapped to the target type a {@link ClassCastException} or a * {@link NullPointerException} is thrown. *
* 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: *

    *
  1. If the value represents {@link #isNull() null} then null is returned. *
  2. If the value is a {@link #isHostObject() host object} then the value is coerced to * {@link #asHostObject() host object value}. *
  3. If the value is a {@link #isString() string} then the value is coerced to {@link String} * or {@link Character}. *
  4. If the value is a {@link #isBoolean() boolean} then the value is coerced to * {@link Boolean}. *
  5. 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()}. *
  6. 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}. *
  7. 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}. *
  8. 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}. *
  9. 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) context.eval("js", "[{}]").as(Object.class)).get(0) instanceof Map;
     * assert context.eval("js", "(function(){})").as(Object.class) instanceof Function;
     * 
* *

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 javaMap = context.eval("js", "{}").as(Map.class);
     * Value polyglotValue = context.asValue(javaMap);
     * 
* * @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); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy