Helpers.java
package autumn.lang.internals;
import autumn.lang.Delegate;
import autumn.lang.Module;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* This class contains utility methods that allow the compiler to generate simpler bytecode.
*
* <p>
* Note: This class is not intended for use directly by programmers.
* </p>
*
* @author Mackenzie High
*/
public final class Helpers
{
/**
* This is the scale of big-decimal literals.
*/
public static final int BIG_DECIMAL_SCALE = 32;
/**
* This method finds a named enum-constant in an array of enum-constants.
*
* <p>
* This method is used in the implementation of the valueOf(String) method in enums.
* </p>
*
* @param constants is the array that may contain the named enum-constant.
* @param name is the name of the enum-constant to find.
* @return the named enum-constant.
* @throws NullPointerException if constants is null.
* @throws NullPointerException if name is null.
* @throws IllegalArgumentException if the named enum-constant is not present in the array.
*/
public static Enum findEnumConstant(final Enum[] constants,
final String name)
{
Preconditions.checkNotNull(constants, "An array of enum-constants was expected.");
Preconditions.checkNotNull(name, "The name of an enum-constant was expected.");
for (Enum constant : constants)
{
if (name.equals(constant.name().trim()))
{
return constant;
}
}
throw new IllegalArgumentException("No such enum-constant: " + name);
}
/**
* This method creates a new immutable-list from an iterable data-structure.
*
* @param iterable is the data-structure that contains the elements of the new list.
* @return the new immutable list.
* @throws NullPointerException if the iterable is null.
*/
public static <T> List<T> newImmutableList(final Iterable<T> iterable)
{
Preconditions.checkNotNull(iterable, "An iterable data-structure was expected.");
final ArrayList<T> list = Lists.newArrayList(iterable);
list.trimToSize();
return Collections.unmodifiableList(list);
}
/**
* This method creates a new immutable-map from an two iterables.
*
* <p>
* The new map will preserve iteration order.
* </p>
*
* @param keys are the keys of the new map in iteration order.
* @param values are the values of the new map in iteration order.
* @return the new immutable list.
* @throws NullPointerException if the keys is null.
* @throws NullPointerException if the values is null.
* @throws IllegalArgumentException if keys.size() != values.size().
*/
public static <K, V> Map<K, V> newImmutableMap(final List<K> keys,
final List<V> values)
{
Preconditions.checkNotNull(keys);
Preconditions.checkNotNull(values);
Preconditions.checkArgument(keys.size() == values.size());
final Map<K, V> map = Maps.newLinkedHashMap();
for (int i = 0; i < keys.size(); i++)
{
map.put(keys.get(i), values.get(i));
}
return Collections.unmodifiableMap(map);
}
/**
* This method searches for a particular function's delegate in a module.
*
* @param module is the module that provides the delegate.
* @param method is the name of the delegated method.
* @return the delegate that refers to the named method.
* @throws IllegalStateException if the module does not contain a method with the given name.
*/
public static Delegate delegate(final Module module,
final String method)
{
Preconditions.checkNotNull(module);
Preconditions.checkNotNull(method);
/**
* If the delegate refers to the special instance() method,
* create a special delegate just it.
*/
if (method.equals("instance"))
{
return new AbstractDelegate()
{
@Override
public void apply(ArgumentStack stack)
throws Throwable
{
stack.clear();
stack.push(module);
}
@Override
public List<Class> parameterTypes()
{
return ImmutableList.of();
}
@Override
public Class returnType()
{
return module.getClass();
}
@Override
public Module module()
{
return module;
}
@Override
public Class owner()
{
return module.getClass();
}
@Override
public String name()
{
return method;
}
};
}
/**
* Otherwise, the delegate must refer to a user-defined function.
*/
for (Delegate d : module.info().functions())
{
if (method.equals(d.name()))
{
return d;
}
}
throw new IllegalStateException(); // TODO: is this right?
}
/**
* This method throws an index-out-of-bounds-exception when an index is out of bounds.
*
* @param index is the index that may be out of bounds.
* @param size is the maximum allowed index plus one.
*/
public static void throwIndexOutOfBoundsException(final int index,
final int size)
{
if (index < 0 || index >= size)
{
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
}
/**
* This method ensures that its argument is not null.
*
* @param value is the value that may be null.
* @throws NullPointerException if value is null.
*/
public static void requireNonNull(final Object value)
{
if (value == null)
{
throw new NullPointerException();
}
}
/**
* This method creates a new big-integer.
*
* <p>
* This method is used to implement big-integer literals.
* </p>
*
* @param value is the string representation of the new big-integer.
* @return the value as a big-integer.
*/
public static BigInteger createBigInteger(final String value)
{
return new BigInteger(value);
}
/**
* This method creates a new big-decimal.
*
* <p>
* This method is used to implement big-decimal literals.
* </p>
*
* @param value is the string representation of the new big-decimal.
* @return the value as a big-decimal.
*/
public static BigDecimal createBigDecimal(final String value)
{
return createBigDecimal(new BigDecimal(value));
}
/**
* This method creates a new properly scaled big-decimal from an existing big-decimal.
*
* <p>
* This method is used to implement big-decimal operators.
* </p>
*
* @param value is big-decimal itself.
* @return the value as a properly scaled big-decimal.
*/
public static BigDecimal createBigDecimal(final BigDecimal value)
{
return value.setScale(BIG_DECIMAL_SCALE, RoundingMode.HALF_EVEN);
}
}