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.objectmapping; 018 019import static io.leangen.geantyref.GenericTypeReflector.box; 020import static io.leangen.geantyref.GenericTypeReflector.erase; 021 022import com.google.auto.value.AutoValue; 023import org.checkerframework.checker.nullness.qual.Nullable; 024import org.spongepowered.configurate.ConfigurationNode; 025import org.spongepowered.configurate.objectmapping.meta.Constraint; 026import org.spongepowered.configurate.objectmapping.meta.NodeResolver; 027import org.spongepowered.configurate.objectmapping.meta.Processor; 028import org.spongepowered.configurate.serialize.SerializationException; 029import org.spongepowered.configurate.serialize.TypeSerializer; 030import org.spongepowered.configurate.util.CheckedFunction; 031import org.spongepowered.configurate.util.UnmodifiableCollections; 032 033import java.lang.reflect.AnnotatedType; 034import java.util.List; 035import java.util.function.Supplier; 036 037/** 038 * Holder for field-specific information. 039 * 040 * @param <I> intermediate type 041 * @param <O> container type 042 * @since 4.0.0 043 */ 044@AutoValue 045@SuppressWarnings("AutoValueImmutableFields") // we don't use guava collections 046public abstract class FieldData<I, O> { 047 048 /** 049 * Create a new data holder for a field. 050 * 051 * @param name field name 052 * @param resolvedFieldType type resolved for specific data 053 * @param constraints constraints to verify on this field 054 * @param processors processors to apply to fields backing this node 055 * @param <I> intermediate type 056 * @param <O> container type 057 * @return new field data 058 * @since 4.0.0 059 */ 060 static <I, O> FieldData<I, O> of(final String name, final AnnotatedType resolvedFieldType, 061 final List<Constraint<?>> constraints, final List<Processor<?>> processors, 062 final Deserializer<I> deserializer, final CheckedFunction<O, @Nullable Object, Exception> serializer, final NodeResolver resolver) { 063 return new AutoValue_FieldData<>(name, resolvedFieldType, 064 UnmodifiableCollections.copyOf(constraints), 065 UnmodifiableCollections.copyOf(processors), 066 deserializer, serializer, resolver); 067 } 068 069 FieldData() { 070 } 071 072 /** 073 * The name of the field. 074 * 075 * @return field name 076 * @since 4.0.0 077 */ 078 public abstract String name(); 079 080 /** 081 * The calculated type of this field within the object type. 082 * 083 * <p>This value has had any possible type parameters resolved using 084 * information available in the context.</p> 085 * 086 * @return the resolved type 087 * @since 4.0.0 088 */ 089 public abstract AnnotatedType resolvedType(); 090 091 abstract List<Constraint<?>> constraints(); 092 093 abstract List<Processor<?>> processors(); 094 095 abstract Deserializer<I> deserializer(); 096 097 abstract CheckedFunction<O, @Nullable Object, Exception> serializer(); 098 099 abstract NodeResolver nodeResolver(); 100 101 /** 102 * Test if an object would be valid for this field. 103 * 104 * @param instance instance to validate 105 * @return true if valid 106 * @since 4.0.0 107 */ 108 public boolean isValid(final Object instance) { 109 try { 110 validate(instance); 111 return true; 112 } catch (final SerializationException ex) { 113 return false; 114 } 115 } 116 117 /** 118 * Try to ensure the provided value is acceptable. 119 * 120 * @param instance field value instance to validate 121 * @throws SerializationException if validation fails 122 * @since 4.0.0 123 */ 124 @SuppressWarnings("unchecked") 125 public void validate(final @Nullable Object instance) throws SerializationException { 126 if (instance != null && !erase(box(resolvedType().getType())).isInstance(instance)) { 127 throw new SerializationException("Object " + instance + " is not of expected type " + resolvedType().getType()); 128 } 129 130 for (Constraint<?> constraint : constraints()) { 131 ((Constraint<Object>) constraint).validate(instance); 132 } 133 } 134 135 TypeSerializer<?> serializerFrom(final ConfigurationNode node) throws SerializationException { 136 final @Nullable TypeSerializer<?> serial = node.options().serializers().get(resolvedType().getType()); 137 if (serial == null) { 138 throw new SerializationException("No TypeSerializer found for field " + name() + " of type " + resolvedType().getType()); 139 } 140 return serial; 141 } 142 143 /** 144 * Use this field's node resolvers to determine a target node. 145 * 146 * @param source parent node 147 * @return resolved node 148 * @since 4.0.0 149 */ 150 public @Nullable ConfigurationNode resolveNode(final ConfigurationNode source) { 151 return this.nodeResolver().resolve(source); 152 } 153 154 /** 155 * A deserialization handler to appropriately place object data into fields. 156 * 157 * @param <I> intermediate data type 158 * @since 4.0.0 159 */ 160 @FunctionalInterface 161 public interface Deserializer<I> { 162 163 /** 164 * Apply either a new value or implicit initializer to the 165 * {@code intermediate} object as appropriate. 166 * 167 * @param intermediate the intermediate container 168 * @param newValue new value to store 169 * @param implicitInitializer the implicit initializer 170 * @since 4.0.0 171 */ 172 void accept(I intermediate, @Nullable Object newValue, Supplier<@Nullable Object> implicitInitializer); 173 } 174 175}