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; 018 019import io.leangen.geantyref.GenericTypeReflector; 020import io.leangen.geantyref.TypeToken; 021import org.checkerframework.checker.nullness.qual.Nullable; 022import org.spongepowered.configurate.serialize.SerializationException; 023import org.spongepowered.configurate.serialize.TypeSerializer; 024import org.spongepowered.configurate.util.CheckedConsumer; 025import org.spongepowered.configurate.util.Types; 026 027import java.lang.reflect.AnnotatedType; 028import java.lang.reflect.Type; 029import java.util.List; 030import java.util.Map; 031import java.util.stream.Collector; 032 033/** 034 * Intermediate node type to reduce need for casting. 035 * 036 * <p>Any methods that return {@link ConfigurationNode} in 037 * {@link ConfigurationNode} should be overridden to return the {@code N} 038 * self-type instead.</p> 039 * 040 * @param <N> self type 041 * @since 4.0.0 042 */ 043public interface ScopedConfigurationNode<N extends ScopedConfigurationNode<N>> extends ConfigurationNode { 044 045 /** 046 * Get a correctly typed instance of this node. 047 * 048 * @return the node type 049 * @since 4.0.0 050 */ 051 N self(); 052 053 /** 054 * {@inheritDoc} 055 */ 056 @Override 057 N appendListNode(); 058 059 /** 060 * {@inheritDoc} 061 */ 062 @Override 063 N copy(); 064 065 /** 066 * {@inheritDoc} 067 */ 068 @Override 069 N node(Object... path); 070 071 /** 072 * {@inheritDoc} 073 */ 074 @Override 075 N node(Iterable<?> path); 076 077 /** 078 * {@inheritDoc} 079 */ 080 @Override 081 @Nullable N parent(); 082 083 /** 084 * {@inheritDoc} 085 */ 086 @Override 087 N from(ConfigurationNode other); 088 089 /** 090 * {@inheritDoc} 091 */ 092 @Override 093 N mergeFrom(ConfigurationNode other); 094 095 /** 096 * {@inheritDoc} 097 */ 098 @Override 099 N set(@Nullable Object value) throws SerializationException; 100 101 /** 102 * {@inheritDoc} 103 */ 104 @Override 105 @SuppressWarnings({"unchecked", "rawtypes"}) // for TypeSerializer.serialize 106 default N set(final Type type, final @Nullable Object value) throws SerializationException { 107 if (value == null) { 108 return this.set(null); 109 } 110 final Type boxed = Types.box(type); 111 final Class<?> erasedType = GenericTypeReflector.erase(boxed); 112 if (!erasedType.isInstance(value)) { 113 throw new SerializationException(this, type, "Got a value of unexpected type " 114 + value.getClass().getName() + ", when the value should be an instance of " + erasedType.getSimpleName()); 115 } 116 117 final @Nullable TypeSerializer<?> serial = this.options().serializers().get(boxed); 118 if (serial != null) { 119 try { 120 ((TypeSerializer) serial).serialize(boxed, value, this.self()); 121 } catch (final SerializationException ex) { 122 ex.initPath(this::path); 123 ex.initType(type); 124 throw ex; 125 } 126 } else if (this.options().acceptsType(value.getClass())) { 127 this.raw(value); // Just write if no applicable serializer exists? 128 } else { 129 throw new SerializationException(this, type, "No serializer available for type " + type); 130 } 131 return this.self(); 132 } 133 134 /** 135 * {@inheritDoc} 136 */ 137 @Override 138 @SuppressWarnings({"unchecked", "rawtypes"}) // for TypeSerializer.serialize 139 default N set(final AnnotatedType type, final @Nullable Object value) throws SerializationException { 140 if (value == null) { 141 return this.set(null); 142 } 143 final Class<?> erasedType = GenericTypeReflector.erase(type.getType()); 144 if (!erasedType.isInstance(value)) { 145 throw new SerializationException(this, type, "Got a value of unexpected type " 146 + value.getClass().getName() + ", when the value should be an instance of " + erasedType.getSimpleName()); 147 } 148 149 final @Nullable TypeSerializer<?> serial = this.options().serializers().get(type); 150 if (serial != null) { 151 try { 152 ((TypeSerializer) serial).serialize(type, value, this); 153 } catch (final SerializationException ex) { 154 ex.initPath(this::path); 155 ex.initType(type); 156 throw ex; 157 } 158 } else if (this.options().acceptsType(value.getClass())) { 159 this.raw(value); // Just write if no applicable serializer exists? 160 } else { 161 throw new SerializationException(this, type, "No serializer available for type " + type); 162 } 163 return this.self(); 164 } 165 166 @Override 167 default <V> N set(final Class<V> type, final @Nullable V value) throws SerializationException { 168 return this.set((Type) type, value); 169 } 170 171 @Override 172 default <V> N set(final TypeToken<V> type, final @Nullable V value) throws SerializationException { 173 return this.set(type.getType(), value); 174 } 175 176 @Override 177 default <V> N setList(final Class<V> elementType, final @Nullable List<V> items) throws SerializationException { 178 ConfigurationNode.super.setList(elementType, items); 179 return this.self(); 180 } 181 182 @Override 183 default <V> N setList(final TypeToken<V> elementType, final @Nullable List<V> items) throws SerializationException { 184 ConfigurationNode.super.setList(elementType, items); 185 return this.self(); 186 } 187 188 /** 189 * {@inheritDoc} 190 */ 191 @Override 192 N raw(@Nullable Object value); 193 194 /** 195 * {@inheritDoc} 196 */ 197 @Override 198 List<N> childrenList(); 199 200 /** 201 * {@inheritDoc} 202 */ 203 @Override 204 Map<Object, N> childrenMap(); 205 206 /** 207 * {@inheritDoc} 208 */ 209 @SuppressWarnings({"unchecked", "rawtypes"}) 210 @Override 211 default <V> Collector<Map.Entry<?, V>, N, N> toMapCollector(final TypeToken<V> valueType) { 212 return (Collector) ConfigurationNode.super.toMapCollector(valueType); 213 } 214 215 /** 216 * {@inheritDoc} 217 */ 218 @SuppressWarnings({"unchecked", "rawtypes"}) 219 @Override 220 default <V> Collector<Map.Entry<?, V>, N, N> toMapCollector(final Class<V> valueType) { 221 return (Collector) ConfigurationNode.super.toMapCollector(valueType); 222 } 223 224 /** 225 * {@inheritDoc} 226 */ 227 @SuppressWarnings({"unchecked", "rawtypes"}) 228 @Override 229 default <V> Collector<V, N, N> toListCollector(final TypeToken<V> valueType) { 230 return (Collector) ConfigurationNode.super.toListCollector(valueType); 231 } 232 233 /** 234 * {@inheritDoc} 235 */ 236 @SuppressWarnings({"unchecked", "rawtypes"}) 237 @Override 238 default <V> Collector<V, N, N> toListCollector(final Class<V> valueType) { 239 return (Collector) ConfigurationNode.super.toListCollector(valueType); 240 } 241 242 /** 243 * Execute an action on this node. This allows performing multiple 244 * operations on a single node without having to clutter up the surrounding 245 * scope. 246 * 247 * @param <E> thrown type 248 * @param action the action to perform on this node 249 * @return this node 250 * @throws E when thrown by callback {@code action} 251 * @since 4.0.0 252 */ 253 default <E extends Exception> N act(final CheckedConsumer<? super N, E> action) throws E { 254 action.accept(this.self()); 255 return this.self(); 256 } 257 258 @Override 259 <V> N hint(RepresentationHint<V> hint, @Nullable V value); 260 261}