/** * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. Copyright (C) 2017 Dan Mulloy *

* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later * version. *

* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. *

* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.comphenix.protocol.wrappers; import com.comphenix.protocol.reflect.EquivalentConverter; import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.accessors.Accessors; import com.comphenix.protocol.reflect.accessors.MethodAccessor; import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; import com.comphenix.protocol.utility.MinecraftReflection; import java.lang.reflect.Array; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.function.Function; import java.util.function.Supplier; /** * Utility class for converters * @author dmulloy2 */ @SuppressWarnings("unchecked") public class Converters { /** * Returns a converter that ignores null elements, so that the underlying converter doesn't have to worry about them. * @param converter Underlying converter * @param Element type * @return An ignore null converter */ public static EquivalentConverter ignoreNull(final EquivalentConverter converter) { return new EquivalentConverter() { @Override public T getSpecific(Object generic) { return generic != null ? converter.getSpecific(generic) : null; } @Override public Object getGeneric(T specific) { return specific != null ? converter.getGeneric(specific) : null; } @Override public Class getSpecificType() { return converter.getSpecificType(); } }; } /** * Returns a converter that passes generic and specific values through without converting. * @param clazz Element class * @param Element type * @return A passthrough converter */ public static EquivalentConverter passthrough(final Class clazz) { return ignoreNull(new EquivalentConverter() { @Override public T getSpecific(Object generic) { return (T) generic; } @Override public Object getGeneric(T specific) { return specific; } @Override public Class getSpecificType() { return clazz; } }); } /** * Creates a simple converter for wrappers with {@code getHandle()} and {@code fromHandle(...)} methods. With Java 8, * converters can be reduced to a single line (see {@link BukkitConverters#getWrappedGameProfileConverter()}). * @param toHandle Function from wrapper to handle (i.e. {@code getHandle()}) * @param fromHandle Function from handle to wrapper (i.e. {@code fromHandle(Object)}) * @param Wrapper type * @return A handle converter */ public static EquivalentConverter handle(final Function toHandle, final Function fromHandle, final Class specificType) { return new EquivalentConverter() { @Override public T getSpecific(Object generic) { return fromHandle.apply(generic); } @Override public Object getGeneric(T specific) { return toHandle.apply(specific); } @Override public Class getSpecificType() { return specificType; } }; } /** * Creates a generic array converter. Converts a NMS object array to and from a wrapper array by converting * each element individually. * * @param nmsClass NMS class * @param converter Underlying converter * @param Generic type * @return An array converter */ public static EquivalentConverter array(final Class nmsClass, final EquivalentConverter converter) { return new EquivalentConverter() { @Override public T[] getSpecific(Object generic) { Object[] array = (Object[]) generic; Class clazz = getSpecificType(); T[] result = clazz.cast(Array.newInstance(clazz.getComponentType(), array.length)); // Unwrap every item for (int i = 0; i < result.length; i++) { result[i] = converter.getSpecific(array[i]); } return result; } @Override public Object getGeneric(T[] specific) { Object[] result = (Object[]) Array.newInstance(nmsClass, specific.length); // Wrap every item for (int i = 0; i < result.length; i++) { result[i] = converter.getGeneric(specific[i]); } return result; } @Override public Class getSpecificType() { return (Class) MinecraftReflection.getArrayClass(converter.getSpecificType()); } }; } public static EquivalentConverter> optional(final EquivalentConverter converter) { return new EquivalentConverter>() { @Override public Object getGeneric(Optional specific) { return specific.map(converter::getGeneric); } @Override public Optional getSpecific(Object generic) { if (generic == null) return Optional.empty(); Optional optional = (Optional) generic; return optional.map(converter::getSpecific); } @Override public Class> getSpecificType() { return (Class>) Optional.empty().getClass(); } }; } public static > EquivalentConverter collection( final EquivalentConverter elementConverter, final Function, C> genericToSpecificCollectionFactory, final Function> specificToGenericCollectionFactory ) { return ignoreNull(new EquivalentConverter() { @Override public Object getGeneric(C specific) { // generics are very cool, thank you java Collection targetCollection = (Collection) specificToGenericCollectionFactory.apply(specific); for (T element : specific) { Object generic = elementConverter.getGeneric(element); if (generic != null) { targetCollection.add(generic); } } return targetCollection; } @Override public C getSpecific(Object generic) { if (generic instanceof Collection) { Collection sourceCollection = (Collection) generic; C targetCollection = genericToSpecificCollectionFactory.apply(sourceCollection); // copy over all elements into a new collection for (Object element : sourceCollection) { T specific = elementConverter.getSpecific(element); if (specific != null) { targetCollection.add(specific); } } return targetCollection; } // not valid return null; } @Override public Class getSpecificType() { return (Class) Collection.class; } }); } public static EquivalentConverter> iterable( final EquivalentConverter elementConverter, final Supplier> specificCollectionFactory, final Supplier> genericCollectionFactory ) { return ignoreNull(new EquivalentConverter>() { @Override public Object getGeneric(Iterable specific) { // generics are very cool, thank you java List targetCollection = (List) genericCollectionFactory.get(); for (T element : specific) { Object generic = elementConverter.getGeneric(element); if (generic != null) { targetCollection.add(generic); } } return targetCollection; } @Override public Iterable getSpecific(Object generic) { if (generic instanceof Iterable) { Iterable sourceCollection = (Iterable) generic; List targetCollection = specificCollectionFactory.get(); // copy over all elements into a new collection for (Object element : sourceCollection) { T specific = elementConverter.getSpecific(element); if (specific != null) { targetCollection.add(specific); } } return targetCollection; } // not valid return null; } @Override public Class> getSpecificType() { return (Class) Iterable.class; } }); } private static MethodAccessor holderGetValue; public static EquivalentConverter holder(final EquivalentConverter converter, final WrappedRegistry registry) { return new EquivalentConverter() { @Override public Object getGeneric(T specific) { Object generic = converter.getGeneric(specific); return registry.getHolder(generic); } @Override public T getSpecific(Object generic) { if (holderGetValue == null) { Class holderClass = MinecraftReflection.getHolderClass(); FuzzyReflection fuzzy = FuzzyReflection.fromClass(holderClass, false); holderGetValue = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract .newBuilder() .parameterCount(0) .banModifier(Modifier.STATIC) .returnTypeExact(Object.class) .build())); } Object value = holderGetValue.invoke(generic); return converter.getSpecific(value); } @Override public Class getSpecificType() { return converter.getSpecificType(); } }; } public static List toList(Iterable iterable) { if (iterable instanceof List) { return (List) iterable; } List result = new ArrayList<>(); if (iterable instanceof Collection) { Collection coll = (Collection) iterable; result.addAll(coll); } else { for (T elem : iterable) { result.add(elem); } } return result; } }