001/* 002 * Configurate 003 * Copyright (C) zml and Configurate contributors 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.spongepowered.configurate.serialize; 018 019import com.google.errorprone.annotations.ForOverride; 020import io.leangen.geantyref.GenericTypeReflector; 021import org.checkerframework.checker.nullness.qual.Nullable; 022import org.spongepowered.configurate.ConfigurationNode; 023import org.spongepowered.configurate.ConfigurationOptions; 024import org.spongepowered.configurate.util.CheckedConsumer; 025 026import java.lang.reflect.AnnotatedType; 027import java.lang.reflect.Type; 028import java.util.Collections; 029import java.util.List; 030 031/** 032 * A serializer for nodes that are 'list-like' (i.e may be stored in nodes where {@link ConfigurationNode#isList()} is 033 * {@literal true}. 034 * 035 * @param <T> the type of collection to serialize 036 * @since 4.1.0 037 */ 038public abstract class AbstractListChildSerializer<T> implements TypeSerializer.Annotated<T> { 039 040 /** 041 * Create a new serializer, only for use by subclasses. 042 * 043 * @since 4.1.0 044 */ 045 protected AbstractListChildSerializer() { 046 } 047 048 @Override 049 public final T deserialize(final AnnotatedType type, final ConfigurationNode node) throws SerializationException { 050 final AnnotatedType entryType = this.elementType(type); 051 final @Nullable TypeSerializer<?> entrySerial = node.options().serializers().get(entryType); 052 if (entrySerial == null) { 053 throw new SerializationException(node, entryType, "No applicable type serializer for type"); 054 } 055 056 if (node.isList()) { 057 final List<? extends ConfigurationNode> values = node.childrenList(); 058 final T ret = this.createNew(values.size(), entryType); 059 for (int i = 0; i < values.size(); ++i) { 060 try { 061 this.deserializeSingle(i, ret, entrySerial.deserialize(entryType, values.get(i))); 062 } catch (final SerializationException ex) { 063 ex.initPath(values.get(i)::path); 064 throw ex; 065 } 066 } 067 return ret; 068 } else { 069 final @Nullable Object unwrappedVal = node.raw(); 070 if (unwrappedVal != null) { 071 final T ret = this.createNew(1, entryType); 072 this.deserializeSingle(0, ret, entrySerial.deserialize(entryType, node)); 073 return ret; 074 } 075 } 076 return this.createNew(0, entryType); 077 } 078 079 @SuppressWarnings({"unchecked", "rawtypes"}) 080 @Override 081 public final void serialize(final AnnotatedType type, final @Nullable T obj, final ConfigurationNode node) throws SerializationException { 082 final AnnotatedType entryType = this.elementType(type); 083 final @Nullable TypeSerializer entrySerial = node.options().serializers().get(entryType); 084 if (entrySerial == null) { 085 throw new SerializationException(node, entryType, "No applicable type serializer for type"); 086 } 087 088 node.raw(Collections.emptyList()); 089 if (obj != null) { 090 this.forEachElement(obj, el -> { 091 final ConfigurationNode child = node.appendListNode(); 092 try { 093 entrySerial.serialize(entryType, el, child); 094 } catch (final SerializationException ex) { 095 ex.initPath(child::path); 096 throw ex; 097 } 098 }); 099 } 100 } 101 102 @Override 103 public @Nullable T emptyValue(final AnnotatedType specificType, final ConfigurationOptions options) { 104 try { 105 return this.createNew(0, this.elementType(specificType)); 106 } catch (final SerializationException ex) { 107 return null; 108 } 109 } 110 111 /** 112 * Given the type of container, provide the expected type of an element. If 113 * the element type is not available, an exception must be thrown. 114 * 115 * @param containerType the type of container with type parameters resolved 116 * to the extent possible. 117 * @return the element type 118 * @throws SerializationException if the element type could not be detected 119 * @since 4.2.0 120 */ 121 @ForOverride 122 protected AnnotatedType elementType(final AnnotatedType containerType) throws SerializationException { 123 return GenericTypeReflector.annotate(this.elementType(containerType.getType())); 124 } 125 126 /** 127 * Given the type of container, provide the expected type of an element. If 128 * the element type is not available, an exception must be thrown. 129 * 130 * @param containerType the type of container with type parameters resolved 131 * to the extent possible. 132 * @return the element type 133 * @throws SerializationException if the element type could not be detected 134 * @since 4.1.0 135 * @deprecated for removal since 4.2.0, override {@link #elementType(AnnotatedType)} instead 136 * to pass through annotation information 137 */ 138 @Deprecated 139 protected Type elementType(final Type containerType) throws SerializationException { 140 throw new IllegalStateException("AbstractListChildSerializer implementations should override elementType(AnnotatedType)"); 141 } 142 143 /** 144 * Create a new instance of the collection. The returned instance must be 145 * mutable, but may have a fixed length. 146 * 147 * @param length the necessary collection length 148 * @param elementType the type of element contained within the collection, 149 * as provided by {@link #elementType(Type)} 150 * @return a newly created collection 151 * @throws SerializationException when an error occurs during the creation 152 * of the collection 153 * @since 4.2.0 154 */ 155 @ForOverride 156 protected T createNew(final int length, final AnnotatedType elementType) throws SerializationException { 157 return this.createNew(length, elementType.getType()); 158 } 159 160 /** 161 * Create a new instance of the collection. The returned instance must be 162 * mutable, but may have a fixed length. 163 * 164 * @param length the necessary collection length 165 * @param elementType the type of element contained within the collection, 166 * as provided by {@link #elementType(Type)} 167 * @return a newly created collection 168 * @throws SerializationException when an error occurs during the creation 169 * of the collection 170 * @since 4.1.0 171 * @deprecated for removal since 4.2.0, override {@link #createNew(int, AnnotatedType)} instead 172 * to pass through annotation information 173 */ 174 @ForOverride 175 @Deprecated 176 protected T createNew(final int length, final Type elementType) throws SerializationException { 177 throw new IllegalStateException("AbstractListChildSerializer implementations should override createNew(int, AnnotatedType)"); 178 } 179 180 /** 181 * Perform the provided action on each element of the provided collection. 182 * 183 * <p>This is equivalent to a foreach loop on the collection 184 * 185 * @param collection the collection to act on 186 * @param action the action to perform 187 * @throws SerializationException when thrown by the underlying action 188 * @since 4.1.0 189 */ 190 @ForOverride 191 protected abstract void forEachElement(T collection, CheckedConsumer<Object, SerializationException> action) throws SerializationException; 192 193 /** 194 * Place a single deserialized value into the collection being deserialized. 195 * 196 * @param index location to set value at 197 * @param collection collection to modify 198 * @param deserialized value to add 199 * @throws SerializationException if object could not be coerced to an 200 * appropriate type. 201 * @since 4.1.0 202 */ 203 @ForOverride 204 protected abstract void deserializeSingle(int index, T collection, @Nullable Object deserialized) throws SerializationException; 205 206}