/*
* Copyright 2014 MovingBlocks
*
* 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 org.terasology.logic.console.commandSystem;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Primitives;
import org.terasology.context.Context;
import org.terasology.entitySystem.entity.EntityRef;
import org.terasology.logic.console.commandSystem.adapter.ParameterAdapterManager;
import org.terasology.logic.console.commandSystem.exceptions.CommandParameterParseException;
import org.terasology.logic.console.commandSystem.exceptions.SuggesterInstantiationException;
import org.terasology.registry.InjectionHelper;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
/**
*/
public final class CommandParameter<T> implements Parameter {
private final String name;
private final Class<T> type;
private final CommandParameterSuggester<T> suggester;
private final boolean required;
private final ParameterAdapterManager parameterAdapterManager;
private CommandParameter(String name, Class<T> typeParam, boolean required, CommandParameterSuggester<T> suggester,
ParameterAdapterManager parameterAdapterManager) {
Preconditions.checkNotNull(name, "The parameter name must not be null!");
if (name.length() <= 0) {
throw new IllegalArgumentException("The parameter name must not be empty!");
}
Preconditions.checkNotNull(typeParam, "The parameter type must not be null!");
Class<T> resultType;
if (typeParam.isPrimitive()) {
if (required) {
resultType = Primitives.wrap(typeParam);
} else {
throw new IllegalArgumentException("An optional parameter must not be primitive!"
+ " Use " + Primitives.wrap(typeParam).getSimpleName()
+ " instead of " + typeParam.getSimpleName() + ".");
}
} else {
resultType = typeParam;
}
Preconditions.checkNotNull(suggester, "The suggester must not be null!");
this.name = name;
this.type = resultType;
this.suggester = suggester;
this.required = required;
this.parameterAdapterManager = parameterAdapterManager;
}
public static <T> CommandParameter single(String name, Class<T> type, boolean required,
CommandParameterSuggester<T> suggester,
Context context) {
if (type.isArray()) {
throw new IllegalArgumentException("The type of a simple CommandParameterDefinition must not be an array!");
}
ParameterAdapterManager parameterAdapterManager = context.get(ParameterAdapterManager.class);
return new CommandParameter(name, type, required, suggester, parameterAdapterManager);
}
public static <T> CommandParameter single(String name, Class<T> type, boolean required,
Class<? extends CommandParameterSuggester<T>> suggesterClass,
Context context)
throws SuggesterInstantiationException {
CommandParameterSuggester<T> suggester = optionallyCreateSuggestor(suggesterClass, context);
return single(name, type, required, suggester, context);
}
private static <T> CommandParameterSuggester<T> optionallyCreateSuggestor(
Class<? extends CommandParameterSuggester<T>> suggestorClass,
Context context) {
if (suggestorClass == null) {
return null;
}
return InjectionHelper.createWithConstructorInjection(suggestorClass, context);
}
@SuppressWarnings("unchecked")
public static CommandParameter single(String name, Class<?> type, boolean required,
Context context) {
return single(name, type, required, (CommandParameterSuggester) null, context);
}
public static <T> CommandParameter array(String name, Class<T> childType, Character arrayDelimiter,
boolean required, CommandParameterSuggester<T> suggester,
Context context) {
if (childType.isArray()) {
throw new IllegalArgumentException("The child type of an array CommandParameterDefinition must not be an array!");
}
Class<?> type = getArrayClass(childType);
ParameterAdapterManager parameterAdapterManager = context.get(ParameterAdapterManager.class);
return new CommandParameter(name, type, required, suggester, parameterAdapterManager);
}
public static <T> CommandParameter array(String name, Class<T> childType, Character arrayDelimiter,
boolean required,
Class<? extends CommandParameterSuggester<T>> suggesterClass,
Context context)
throws SuggesterInstantiationException {
CommandParameterSuggester<T> suggester = optionallyCreateSuggestor(suggesterClass, context);
return array(name, childType, arrayDelimiter, required, suggester, context);
}
@SuppressWarnings("unchecked")
public static CommandParameter array(String name, Class<?> childType, Character arrayDelimiter,
boolean required, Context context) {
return array(name, childType, arrayDelimiter, required, (CommandParameterSuggester) null,
context);
}
public static <T> CommandParameter array(String name, Class<T> childType, boolean required,
CommandParameterSuggester<T> suggester,
Context context) {
return array(name, childType, null, required, suggester, context);
}
public static <T> CommandParameter array(String name, Class<T> childType, boolean required,
Class<? extends CommandParameterSuggester<T>> suggesterClass,
Context context)
throws SuggesterInstantiationException {
CommandParameterSuggester<T> suggester = optionallyCreateSuggestor(suggesterClass, context);
return array(name, childType, required, suggester, context);
}
@SuppressWarnings("unchecked")
public static CommandParameter array(String name, Class<?> childType, boolean required,
Context context) {
return array(name, childType, required, (CommandParameterSuggester) null, context);
}
public static <T> CommandParameter varargs(String name, Class<T> childType, boolean required,
CommandParameterSuggester<T> suggester,
Context context) {
return array(name, childType, required, suggester, context);
}
public static <T> CommandParameter varargs(String name, Class<T> childType, boolean required,
Class<? extends CommandParameterSuggester<T>> suggesterClass,
Context context)
throws SuggesterInstantiationException {
return varargs(name, childType, required, optionallyCreateSuggestor(suggesterClass, context), context);
}
@SuppressWarnings("unchecked")
public static CommandParameter varargs(String name, Class<?> childType, boolean required,
Context context) {
return varargs(name, childType, required, (CommandParameterSuggester) null, context);
}
/**
* @param clazz The child class of the array class returned
* @return The array class of {@code clazz}
*/
@SuppressWarnings("unchecked")
private static <T> Class<? extends T[]> getArrayClass(Class<T> clazz) {
return (Class<? extends T[]>) Array.newInstance(clazz, 0).getClass();
}
public Object getValue(String param) throws CommandParameterParseException {
if (!isArray()) {
return parse(param);
} else {
return getArrayValue(Arrays.asList(param));
}
}
public Object getArrayValue(List<String> params) throws CommandParameterParseException {
Object arrayInstance = Array.newInstance(getType(), params.size());
for (int i = 0; i < params.size(); ++i) {
Array.set(arrayInstance, i, parse(params.get(i)));
}
return arrayInstance;
}
private Object parse(String string) throws CommandParameterParseException {
Class<?> childType = getTypeNotPrimitive();
if (parameterAdapterManager.isAdapterRegistered(childType)) {
try {
return parameterAdapterManager.parse(childType, string);
} catch (Error | Exception e) {
throw new CommandParameterParseException("An error occurred while parsing " + getType().getCanonicalName(), e, string);
}
}
throw new CommandParameterParseException("Cannot parse a " + childType.getCanonicalName(), string);
}
public String convertToString(Object object) {
return parameterAdapterManager.convertToString(object, (Class<? super Object>) getType());
}
public Set<T> suggest(EntityRef sender, Object... parameters) {
return suggester.suggest(sender, parameters);
}
public String getUsage() {
String simpleTypeName = getType().getSimpleName();
StringBuilder usage = new StringBuilder(simpleTypeName);
if (hasName()) {
usage.append(' ').append(getName());
}
if (isArray()) {
usage.insert(0, '(');
usage.append("...)");
} else if (isRequired()) {
usage.insert(0, '<');
usage.append('>');
} else {
usage.insert(0, '(');
usage.append(')');
}
return usage.toString();
}
public boolean isArray() {
return type.isArray();
}
public boolean hasName() {
return name.length() >= 0;
}
public String getName() {
return name;
}
public Class<?> getType() {
if (type.isArray()) {
return type.getComponentType();
}
return type;
}
public Class<?> getTypeNotPrimitive() {
Class<?> componentType = getType();
if (componentType.isPrimitive()) {
return Primitives.wrap(componentType);
} else {
return componentType;
}
}
public Class<T> getTypeRaw() {
return type;
}
public CommandParameterSuggester<T> getSuggester() {
return suggester;
}
public boolean isRequired() {
return required && !isArray();
}
@Override
public Optional<? extends Class<?>> getProvidedType() {
return Optional.of(getTypeRaw());
}
}