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.meta; 018 019import org.checkerframework.checker.nullness.qual.Nullable; 020import org.spongepowered.configurate.BasicConfigurationNode; 021import org.spongepowered.configurate.ConfigurationNode; 022 023import java.lang.annotation.Annotation; 024import java.lang.reflect.AnnotatedElement; 025 026/** 027 * A function to resolve nodes for a specific field. 028 * 029 * @since 4.0.0 030 */ 031public interface NodeResolver { 032 033 /** 034 * Indicates that a field should be explicitly skipped. 035 * 036 * @since 4.0.0 037 */ 038 NodeResolver SKIP_FIELD = parent -> null; 039 040 /** 041 * Given a parent node, resolve an appropriate child. 042 * 043 * @param parent parent node 044 * @return child node, or null if the node should not be deserialized. 045 * @since 4.0.0 046 */ 047 @Nullable ConfigurationNode resolve(ConfigurationNode parent); 048 049 /** 050 * Provides fields. 051 * 052 * @since 4.0.0 053 */ 054 interface Factory { 055 056 /** 057 * Create a function that resolves a child node from its parent. 058 * 059 * @param name field name 060 * @param element annotations on the field 061 * @return {@code null} to continue, {@link #SKIP_FIELD} to stop further 062 * processing and exclude this field from serialization, or a 063 * resolver for a node. 064 * @since 4.0.0 065 */ 066 @Nullable NodeResolver make(String name, AnnotatedElement element); 067 } 068 069 /** 070 * Creates resolvers that provide the key of the containing node for values. 071 * 072 * @return key-based resolver 073 * @since 4.0.0 074 */ 075 static NodeResolver.Factory nodeKey() { 076 return (name, element) -> { 077 if (element.isAnnotationPresent(NodeKey.class)) { 078 return node -> BasicConfigurationNode.root(node.options()).raw(node.key()); 079 } 080 return null; 081 }; 082 } 083 084 /** 085 * Creates resolvers that get the node at a key defined by {@link Setting}. 086 * 087 * @return a factory that will extract keys from a provided annotation 088 * @since 4.0.0 089 */ 090 static NodeResolver.Factory keyFromSetting() { 091 return (name, element) -> { 092 if (element.isAnnotationPresent(Setting.class)) { 093 final String key = element.getAnnotation(Setting.class).value(); 094 if (!key.isEmpty()) { 095 return node -> node.node(key); 096 } 097 } 098 return null; 099 }; 100 } 101 102 /** 103 * A resolver that skips any field not annotated with {@code annotation}. 104 * 105 * @param annotation annotation to require 106 * @return a new resolver 107 * @since 4.0.0 108 */ 109 static NodeResolver.Factory onlyWithAnnotation(final Class<? extends Annotation> annotation) { 110 return (name, element) -> { 111 if (!element.isAnnotationPresent(annotation)) { 112 return NodeResolver.SKIP_FIELD; 113 } 114 return null; 115 }; 116 } 117 118 /** 119 * A resolver that will skip any field not annotated with {@link Setting}. 120 * 121 * @return new resolver restricting fields 122 * @since 4.0.0 123 */ 124 static NodeResolver.Factory onlyWithSetting() { 125 return onlyWithAnnotation(Setting.class); 126 } 127 128 /** 129 * A resolver that uses the containing node of a field. 130 * 131 * <p>This can be used to combine multiple Java objects into one 132 * configuration node.</p> 133 * 134 * @return new resolver using containing field value 135 * @since 4.0.0 136 */ 137 static NodeResolver.Factory nodeFromParent() { 138 return (name, element) -> { 139 final @Nullable Setting setting = element.getAnnotation(Setting.class); 140 if (setting != null && setting.nodeFromParent()) { 141 return node -> node; 142 } 143 return null; 144 }; 145 } 146 147}