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

net.sandius.rembulan.Conversions Maven / Gradle / Ivy

There is a newer version: 1.0.3
Show newest version
/*
 * Copyright 2016 Miroslav Janíček
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.sandius.rembulan;

import java.util.Arrays;

/**
 * Static methods implementing Lua value conversions.
 */
public final class Conversions {

	private Conversions() {
		// not to be instantiated
	}

	/**
	 * Returns the numerical value of the string {@code s}, or {@code null} if
	 * {@code s} does not have a numerical value.
	 *
	 * 

If {@code s} is a valid Lua integer literal with optional sign, the numerical * value is the corresponding integer; if {@code s} is a valid Lua float literal with * optional sign, the numerical value is the corresponding float. Otherwise, the {@code s} * does not have a numerical value.

* *

Leading and trailing whitespace in {@code s} is ignored by this method.

* *

Numbers returned by this method are in the canonical representation.

* * @param s string to convert to numerical value, may be {@code null} * @return a number representing the numerical value of {@code s} (in the canonical * representation), or {@code null} if {@code s} does not have a numerical value */ public static Number numericalValueOf(ByteString s) { String trimmed = s.toString().trim(); try { return Long.valueOf(LuaFormat.parseInteger(trimmed)); } catch (NumberFormatException ei) { try { return Double.valueOf(LuaFormat.parseFloat(trimmed)); } catch (NumberFormatException ef) { return null; } } } /** * Returns the numerical value of the object {@code o}, or {@code null} if {@code o} * does not have a numerical value. * *

If {@code o} is already a number, returns {@code o} cast to number. If {@code o} * is a string, returns its numerical value (see {@link #numericalValueOf(ByteString)}). * Otherwise, returns {@code null}.

* *

This method differs from {@link #arithmeticValueOf(Object)} in that it * preserves the numerical value representation of coerced strings. For use in arithmetic * operations following Lua's argument conversion rules, use that method instead.

* *

Numbers returned by this method are not necessarily in the canonical representation.

* * @param o object to convert to numerical value, may be {@code null} * @return number representing the numerical value of {@code o} (not necessarily in * the canonical representation), of {@code null} if {@code o} does not have * a numerical value * * @see #arithmeticValueOf(Object) */ public static Number numericalValueOf(Object o) { if (o instanceof Number) return (Number) o; else if (o instanceof ByteString) return numericalValueOf((ByteString) o); else if (o instanceof String) return numericalValueOf(ByteString.of((String) o)); else return null; } /** * Returns the numerical value of {@code o}, throwing a {@link ConversionException} * if {@code o} does not have a numerical value. * *

The conversion rules are those of {@link #numericalValueOf(Object)}; the only difference * is that this method throws an exception rather than returning {@code null} to signal * errors.

* *

Numbers returned by this method are not necessarily in the canonical representation.

* * @param o object to convert to numerical value, may be {@code null} * @param name value name for error reporting, may be {@code null} * @return number representing the numerical value of {@code o} (not necessarily in * the canonical representation), guaranteed to be non-{@code null} * * @throws ConversionException if {@code o} is not a number or string convertible * to number. * * @see #numericalValueOf(Object) */ public static Number toNumericalValue(Object o, String name) { Number n = numericalValueOf(o); if (n == null) { throw new ConversionException((name != null ? name : "value") + " must be a number"); } else { return n; } } /** * Returns the number {@code n} in its canonical representation, * i.e. a {@link java.lang.Long} if {@code n} is a Lua integer, or a {@link java.lang.Double} * if {@code n} is a Lua float. * * @param n number to convert to canonical representation, must not be {@code null} * @return an instance of {@code Long} if {@code n} is an integer, or an instance * of {@code Double} if {@code n} is a float * * @throws NullPointerException if {@code n} is {@code null} */ public static Number toCanonicalNumber(Number n) { if (n instanceof Long || n instanceof Double) { // already in canonical representation return n; } else if (n instanceof Float) { // re-box return Double.valueOf(n.doubleValue()); } else { // re-box return Long.valueOf(n.longValue()); } } /** * Returns the value {@code o} to its canonical representation. * *

For numbers, this method is equivalent to {@link #toCanonicalNumber(Number)}. * If {@code o} is a {@link String java.lang.String}, it is wrapped into a byte * string by {@link ByteString#of(String)}. Otherwise, {@code o} is in canonical * representation.

* *

This method is intended for use at the Java → Lua boundary, and whenever * it is not certain that {@code o} is in a canonical representation when a canonical * representation is required.

* * @param o value to convert to canonical representation, may be {@code null} * @return {@code o} converted to canonical representation */ public static Object canonicalRepresentationOf(Object o) { if (o instanceof Number) return toCanonicalNumber((Number) o); else if (o instanceof String) return ByteString.of((String) o); else return o; } /** * Returns the value {@code o} in its Java representation. * *

If {@code o} is a {@link ByteString}, returns {@code o} as a {@code java.lang.String} * (using {@link ByteString#toString()}. Otherwise, returns {@code o}.

* *

This method is intended for use at the Lua → Java boundary for interoperating * with Java code unaware of (or not concerned with) the interpretation of Lua * strings as sequences of bytes.

* * @param o value to convert to Java representation, may be {@code null} * @return {@code o} converted to a {@code java.lang.String} if {@code o} is * a byte string, {@code o} otherwise */ public static Object javaRepresentationOf(Object o) { if (o instanceof ByteString) return o.toString(); else return o; } /** * Modifies the contents of the array {@code values} by converting all values to * their canonical representations. * * @param values values to convert to their canonical representations, must not be {@code null} * * @throws NullPointerException if {@code values} is {@code null} * * @see #canonicalRepresentationOf(Object) */ public static void toCanonicalValues(Object[] values) { for (int i = 0; i < values.length; i++) { Object v = values[i]; values[i] = canonicalRepresentationOf(v); } } /** * Modifies the contents of the array {@code values} by converting all values to * their Java representations. * *

This method is intended for use at the Lua → Java boundary for interoperating * with Java code unaware of (or not concerned with) the interpretation of Lua * strings as sequences of bytes.

* * @param values values to convert to their Java representations, must not be {@code null} * * @throws NullPointerException if {@code values} is {@code null} * * @see #javaRepresentationOf(Object) */ public static void toJavaValues(Object[] values) { for (int i = 0; i < values.length; i++) { Object v = values[i]; values[i] = javaRepresentationOf(v); } } /** * Returns a copy of the array {@code values} with all values converted to their * canonical representation. * * @param values values to convert to their canonical representation, must not be {@code null} * @return a copy of {@code values} with all elements converted to canonical representation * * @see #canonicalRepresentationOf(Object) */ public static Object[] copyAsCanonicalValues(Object[] values) { values = Arrays.copyOf(values, values.length); toCanonicalValues(values); return values; } /** * Returns a copy of the array {@code values} with all values converted to their * Java representation. * *

This method is intended for use at the Lua → Java boundary for interoperating * with Java code unaware of (or not concerned with) the interpretation of Lua * strings as sequences of bytes.

* * @param values values to convert to their Java representation, must not be {@code null} * @return a copy of {@code values} with all elements converted to their Java representation * * @see #javaRepresentationOf(Object) */ public static Object[] copyAsJavaValues(Object[] values) { values = Arrays.copyOf(values, values.length); toJavaValues(values); return values; } /** * Normalises the number {@code n} so that it may be used as a key in a Lua * table. * *

If {@code n} has an integer value i, returns the canonical representation * of i; otherwise, returns the canonical representation of {@code n} * (see {@link #toCanonicalNumber(Number)}).

* * @param n number to normalise, must not be {@code null} * @return an canonical integer if {@code n} has an integer value, * the canonical representation of {@code n} otherwise * * @throws NullPointerException if {@code n} is {@code null} */ public static Number normaliseKey(Number n) { Long i = integerValueOf(n); return i != null ? i : toCanonicalNumber(n); } /** * Normalises the argument {@code o} so that it may be used safely as a key * in a Lua table. * *

If {@code o} is a number, returns the number normalised (see {@link #normaliseKey(Number)}. * If {@code o} is a {@code java.lang.String}, returns {@code o} as a byte string using * {@link ByteString#of(String)}. Otherwise, returns {@code o}.

* * @param o object to normalise, may be {@code null} * @return normalised number if {@code o} is a number, {@code o} as byte string if * {@code o} is a {@code java.lang.String}, {@code o} otherwise */ public static Object normaliseKey(Object o) { if (o instanceof Number) return normaliseKey((Number) o); else if (o instanceof String) return ByteString.of((String) o); else return o; } /** * Returns the arithmetic value of the object {@code o}, or {@code null} if {@code o} * does not have an arithmetic value. * *

If {@code o} is a number, then that number is its arithmetic value. If {@code o} * is a string that has a numerical value (see {@link #numericalValueOf(ByteString)}), * its arithmetic value is the numerical value converted to a float. Otherwise, * {@code o} does not have an arithmetic value.

* *

Note that this method differs from {@link #numericalValueOf(Object)} in that it * coerces strings convertible to numbers into into floats rather than preserving * their numerical value representation, and also note that this conversion happens * after the numerical value has been determined. Most significantly,

* *
	 *     Conversions.arithmeticValueOf("-0")
	 * 
* *

yields {@code 0.0} rather than {@code 0} (as would be the case with * {@code numericalValueOf("-0")}), or {@code -0.0} (it would in the case if the string * was parsed directly as a float).

* *

Numbers returned by this method are not necessarily in the canonical representation.

* * @param o object to convert to arithmetic value, may be {@code null} * * @return number representing the arithmetic value of {@code o} (not necessarily in * the canonical representation), or {@code null} if {@code o} does not have * an arithmetic value * * @see #numericalValueOf(Object) */ public static Number arithmeticValueOf(Object o) { if (o instanceof Number) { return (Number) o; } else { Number n = numericalValueOf(o); return n != null ? floatValueOf(n) : null; } } /** * Returns the integer value of the number {@code n}, or {@code null} if {@code n} * does not have an integer value. * *

{@code n} has an integer value if and only if the number it denotes can be represented * as a signed 64-bit integer. That integer is then the integer value of {@code n}. * In other words, if {@code n} is a float, it has an integer value if and only if * it can be converted to a {@code long} without loss of precision.

* * @param n number to convert to integer, must not be {@code null} * @return a {@code Long} representing the integer value of {@code n}, * or {@code null} if {@code n} does not have an integer value * * @throws NullPointerException if {@code n} is {@code null} * @see LuaMathOperators#hasExactIntegerRepresentation(double) */ public static Long integerValueOf(Number n) { if (n instanceof Double || n instanceof Float) { double d = n.doubleValue(); return LuaMathOperators.hasExactIntegerRepresentation(d) ? Long.valueOf((long) d) : null; } else if (n instanceof Long) { return (Long) n; } else { return Long.valueOf(n.longValue()); } } /** * Returns the integer value of the number {@code n}, throwing * a {@link NoIntegerRepresentationException} if {@code n} does not have an integer value. * *

This is a variant of {@link #integerValueOf(Number)}; the difference is that * this method throws an exception rather than returning {@code null} to signal that * {@code n} does not have an integer value, and that this method returns the unboxed * integer value of {@code n} (as a {@code long}).

* * @param n object to be converted to integer, must not be {@code null} * @return integer value of {@code n} * * @throws NoIntegerRepresentationException if {@code n} does not have an integer value * @throws NullPointerException if {@code n} is {@code null} */ public static long toIntegerValue(Number n) { Long l = integerValueOf(n); if (l != null) { return l.longValue(); } else { throw new NoIntegerRepresentationException(); } } /** * Returns the integer value of the object {@code o}, or {@code null} if {@code o} * does not have an integer value. * *

The integer value of {@code o} is the integer value of its numerical value * (see {@link #numericalValueOf(Object)}), when it exists.

* * @param o object to be converted to integer, may be {@code null} * @return a {@code Long} representing the integer value of {@code o}, * or {@code null} if {@code o} does not have a integer value * * @see #integerValueOf(Number) */ public static Long integerValueOf(Object o) { Number n = numericalValueOf(o); return n != null ? integerValueOf(n) : null; } /** * Returns the integer value of the object {@code o}, throwing * a {@link NoIntegerRepresentationException} if {@code o} does not have an integer value. * *

This is a variant of {@link #integerValueOf(Object)}; the difference is that * this method throws an exception rather than returning {@code null} to signal that * {@code o} does not have an integer value, and that this method returns the unboxed * integer value of {@code o} (as a {@code long}).

* * @param o object to be converted to integer, may be {@code null} * @return integer value of {@code n} * * @throws NoIntegerRepresentationException if {@code o} does not have an integer value */ public static long toIntegerValue(Object o) { Long l = integerValueOf(o); if (l != null) { return l.longValue(); } else { throw new NoIntegerRepresentationException(); } } /** * Returns the float value of the number {@code n}. * *

The float value of {@code n} is its numerical value converted to a Lua float.

* * @param n the number to convert to float, must not be {@code null} * @return the float value of {@code n}, * guaranteed to be non-{@code null} * * @throws NullPointerException if {@code n} is {@code null} */ public static Double floatValueOf(Number n) { return n instanceof Double ? (Double) n : Double.valueOf(n.doubleValue()); } /** * Returns the boolean value of the object {@code o}. * *

The boolean value of {@code o} is {@code false} if and only if {@code o} is nil * (i.e., {@code null}) or false (i.e., a {@link Boolean} {@code b} such * that {@code b.booleanValue() == false}). * * @param o object to convert to boolean, may be {@code null} * @return {@code false} if {@code o} is nil or false, {@code true} otherwise */ public static boolean booleanValueOf(Object o) { return !(o == null || (o instanceof Boolean && !((Boolean) o).booleanValue())); } /** * Returns the string value of the number {@code n}. * *

The string value of integers is the result of {@link LuaFormat#toByteString(long)} * on their numerical value; similarly the string value of floats is the result * of {@link LuaFormat#toByteString(double)} on their numerical value. * * @param n number to be converted to string, must not be {@code null} * @return string value of {@code n}, guaranteed to be non-{@code null} * * @throws NullPointerException if {@code n} is {@code null} */ public static ByteString stringValueOf(Number n) { if (n instanceof Double || n instanceof Float) { return LuaFormat.toByteString(n.doubleValue()); } else { return LuaFormat.toByteString(n.longValue()); } } /** * Returns the string value of the object {@code o}, or {@code null} if {@code o} does * not have a string value. * *

If {@code o} is a string, that is the string value. If {@code o} is a number, * returns the string value of that number (see {@link #stringValueOf(Number)}). * Otherwise, {@code o} does not have a string value. * * @param o object to be converted to string, may be {@code null} * @return string value of {@code o}, or {@code null} if {@code o} does not have * a string value */ public static ByteString stringValueOf(Object o) { if (o instanceof ByteString) return (ByteString) o; else if (o instanceof Number) return stringValueOf((Number) o); else if (o instanceof String) return ByteString.of((String) o); else return null; } /** * Converts the object {@code o} to a human-readable string format. * *

The conversion rules are the following: * *

    *
  • If {@code o} is a {@code string} or {@code number}, returns the string value * of {@code o};
  • *
  • if {@code o} is nil (i.e., {@code null}), returns {@code "nil"};
  • *
  • if {@code o} is a {@code boolean}, returns {@code "true"} if {@code o} is true * or {@code "false"} if {@code o} is false;
  • *
  • otherwise, returns the string of the form {@code "TYPE: 0xHASH"}, where * TYPE is the Lua type of {@code o}, and HASH * is the {@link Object#hashCode()} of {@code o} * in hexadecimal format. *
  • *
* *

Note that this method ignores the object's {@code toString()} method * and its {@code __tostring} metamethod. * * @param o the object to be converted to string, may be {@code null} * @return human-readable string representation of {@code o} * * @see #stringValueOf(Object) */ public static ByteString toHumanReadableString(Object o) { if (o == null) return LuaFormat.NIL; else if (o instanceof ByteString) return (ByteString) o; else if (o instanceof Number) return stringValueOf((Number) o); else if (o instanceof Boolean) return LuaFormat.toByteString(((Boolean) o).booleanValue()); else if (o instanceof String) return ByteString.of((String) o); else return ByteString.of(String.format("%s: %#010x", PlainValueTypeNamer.INSTANCE.typeNameOf(o), o.hashCode())); } /** * Converts a {@code Throwable} {@code t} to an error object. * *

If {@code t} is a {@link LuaRuntimeException}, the result of this operation * is the result of its {@link LuaRuntimeException#getErrorObject()}. Otherwise, * the result is {@link Throwable#getMessage()}. * * @param t throwable to convert to error object, must not be {@code null} * @return error object represented by {@code t} * * @throws NullPointerException if {@code t} is {@code null} */ public static Object toErrorObject(Throwable t) { if (t instanceof LuaRuntimeException) { return ((LuaRuntimeException) t).getErrorObject(); } else { return t.getMessage(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy