package net.minecraft.command.type.management;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.collections4.trie.PatriciaTrie;
import net.minecraft.command.CommandException;
import net.minecraft.command.IPermission;
import net.minecraft.command.SyntaxErrorException;
import net.minecraft.command.arg.ArgWrapper;
import net.minecraft.command.collections.Relations;
import net.minecraft.command.completion.ITabCompletion;
import net.minecraft.command.parser.Parser;
import net.minecraft.command.type.management.relations.Relation.Attribute;
public abstract class Convertable<T, W, E extends CommandException>
{
private static final PatriciaTrie<Convertable<?, ?, ?>> convertables = new PatriciaTrie<>();
private final Map<Convertable<?, ?, ?>, Converter<?, T, ?>> convertableFrom = new HashMap<>();
protected final Map<Convertable<?, ?, ? extends E>, Converter<T, ?, ? extends E>> convertableTo = new HashMap<>();
private final List<Attribute> attributes = new ArrayList<>();
private final Map<Convertable<?, ?, ?>, Integer> dist = new HashMap<>();
public final String name;
public Convertable(final String name)
{
this.name = name;
}
public void init()
{
if (convertables.put(this.name, this) != null)
throw new IllegalArgumentException("Convertable with name '" + this.name + "' already registered");
}
public Set<? extends Convertable<?, ?, ?>> convertableTo()
{
return Collections.unmodifiableSet(this.convertableTo.keySet());
}
private final void setDist(final Convertable<?, ?, ?> target, final int dist)
{
if (dist == 0)
this.dist.remove(target);
else
this.dist.put(target, dist);
}
public final int getDist(final Convertable<?, ?, ?> target)
{
final Integer dist = this.dist.get(target);
return dist == null ? 0 : dist;
}
private static final <F, R, E extends CommandException> Converter<F, R, E> createFailingConverter(final Convertable<F, ?, E> source, final Convertable<R, ?, ?> target, final int dist)
{
return new Converter<F, R, E>()
{
@Override
public R convert(final F toConvert)
{
throw new IllegalStateException("Multiple converters from '" + source.name + "' to '" + target.name + "' with same fallback-level (" + dist + ") are registered: No well defined priority");
}
};
}
public void clear()
{
this.convertableFrom.clear();
this.convertableTo.clear();
this.attributes.clear();
this.dist.clear();
}
public final <R> void addConverter(final Convertable<R, ?, ? extends E> target, final Converter<T, R, ? extends E> converter)
{
this.addConverter(target, converter, 0);
}
public final <R> void addConverter(final Convertable<R, ?, ? extends E> target, Converter<T, R, ? extends E> converter, final int dist)
{
if (this == target)
{
if (dist == 0)
throw new IllegalArgumentException("Cannot register identity converter (for convertable '" + this.name + "')");
return;
}
if (this.convertableTo.containsKey(target))
{
final int oldFallbackLevel = this.getDist(target);
if (oldFallbackLevel < dist)
return;
if (oldFallbackLevel == dist)
{
if (dist == 0)
throw new IllegalArgumentException("Converter from '" + this.name + "' to '" + target.name + "' already registered");
converter = createFailingConverter(this, target, dist);
}
}
else
this.adjustCompletions(target);
this.setDist(target, dist);
this.convertableTo.put(target, converter);
target.convertableFrom.put(this, converter);
for (final Attribute attSource : this.attributes)
{
for (final Attribute attTarget : target.attributes)
attSource.apply(this, target, converter, dist, attTarget);
attSource.apply(this, target, converter, dist, Relations.idAttribute);
}
for (final Attribute attTarget : target.attributes)
Relations.idAttribute.apply(this, target, converter, dist, attTarget);
}
// Checked...
@SuppressWarnings("unchecked")
public final <R> R convertTo(final T toConvert, final Convertable<R, ?, ?> target) throws E, SyntaxErrorException
{
if (this == target)
return (R) toConvert;
final Converter<T, R, ? extends E> converter = this.getConverter(target);
if (converter == null)
throw new SyntaxErrorException("Cannot convert " + this.name + " to " + target.name + ".");
return converter.convert(toConvert);
}
// Checked...
@SuppressWarnings("unchecked")
public final <R> R convertTo(final Parser parser, final T toConvert, final Convertable<R, ?, ?> target) throws E, SyntaxErrorException
{
if (this == target)
return (R) toConvert;
final Converter<T, R, ? extends E> converter = this.getConverter(target);
if (converter == null)
throw parser.SEE("Cannot convert " + this.name + " to " + target.name + ".", false);
return converter.convert(toConvert);
}
public final boolean convertableFrom(final Convertable<?, ?, ?> source)
{
return this == source || this.convertableFrom.containsKey(source);
}
public abstract W convertFrom(Parser parser, ArgWrapper<?> toConvert) throws SyntaxErrorException;
// Checked...
@SuppressWarnings("unchecked")
public final <R> Converter<T, R, ? extends E> getConverter(final Convertable<R, ?, ?> target)
{
return (Converter<T, R, ? extends E>) this.convertableTo.get(target);
}
public static Convertable<?, ?, ?> get(final String name)
{
return convertables.get(name);
}
public static void clearAll()
{
for (final Convertable<?, ?, ?> convertable : convertables.values())
convertable.clear();
convertables.clear();
}
public final void addAttribute(final Attribute attribute)
{
this.attributes.add(attribute);
this.initAtt(attribute);
}
public final void initAtt(final Attribute attribute)
{
for (final Entry<Convertable<?, ?, ?>, Converter<?, T, ?>> data : this.convertableFrom.entrySet())
this.initAttSource(data.getKey(), data.getValue(), attribute);
for (final Entry<Convertable<?, ?, ? extends E>, Converter<T, ?, ? extends E>> data : this.convertableTo.entrySet())
this.initAttTarget(data.getKey(), data.getValue(), attribute);
}
// Checked...
@SuppressWarnings("unchecked")
private final <R, U extends CommandException> void initAttSource(final Convertable<R, ?, U> source, final Converter<?, T, ?> converterTmp, final Attribute attTarget)
{
final Converter<R, T, ? extends U> converter = (Converter<R, T, ? extends U>) converterTmp;
for (final Attribute attSource : source.attributes)
attSource.apply(source, (Convertable<T, ?, ? extends U>) this, converter, attTarget);
Relations.idAttribute.apply(source, (Convertable<T, ?, ? extends U>) this, converter, attTarget);
}
// Checked...
@SuppressWarnings("unchecked")
private final <R> void initAttTarget(final Convertable<R, ?, ? extends E> target, final Converter<T, ?, ? extends E> converterTmp, final Attribute attSource)
{
final Converter<T, R, ? extends E> converter = (Converter<T, R, ? extends E>) converterTmp;
for (final Attribute attTarget : target.attributes)
attSource.apply(this, target, converter, attTarget);
attSource.apply(this, target, converter, Relations.idAttribute);
}
@SuppressWarnings("unused")
public void addPossibleSelector(final ITabCompletion tc, final IPermission permission)
{
}
@SuppressWarnings("unused")
public void addPossibleOperator(final ITabCompletion tc, final IPermission permission)
{
}
@SuppressWarnings("unused")
public void adjustCompletions(final Convertable<?, ?, ?> target)
{
}
}