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.spongepowered.configurate.CommentedConfigurationNodeIntermediary; 020import org.spongepowered.configurate.ConfigurationNode; 021 022import java.lang.annotation.Annotation; 023import java.lang.reflect.AnnotatedElement; 024import java.lang.reflect.Type; 025import java.util.ResourceBundle; 026 027/** 028 * Performs a transformation on a value annotated with a specific type. 029 * 030 * @since 4.0.0 031 */ 032@FunctionalInterface 033public interface Processor<V> { 034 035 /** 036 * Transform the output node on write. 037 * 038 * @param value source value 039 * @param destination destination node 040 * @since 4.0.0 041 */ 042 void process(V value, ConfigurationNode destination); 043 044 /** 045 * Provider to, given an annotation instance and the type it's on, 046 * create a {@link Processor}. If you don't need access to the other 047 * annotations on the field, you can also choose the simpler {@link Factory}. 048 * 049 * @param <A> annotation type 050 * @param <T> handled value type 051 * @since 4.0.0 052 */ 053 @FunctionalInterface 054 interface AdvancedFactory<A extends Annotation, T> { 055 056 /** 057 * Create a new processor given the annotation and data type. 058 * 059 * @param data annotation type on record field 060 * @param value declared field type 061 * @param container container holding the field, with its annotations 062 * @return new processor 063 * @since 4.0.0 064 */ 065 Processor<T> make(A data, Type value, AnnotatedElement container); 066 067 } 068 069 /** 070 * Provider to, given an annotation instance and the type it's on, 071 * create a {@link Processor}. 072 * 073 * @param <A> annotation type 074 * @param <T> handled value type 075 * @since 4.0.0 076 */ 077 @FunctionalInterface 078 interface Factory<A extends Annotation, T> extends AdvancedFactory<A, T> { 079 080 /** 081 * Create a new processor given the annotation and data type. 082 * 083 * @param data annotation type on record field 084 * @param value declared field type 085 * @return new processor 086 * @since 4.0.0 087 */ 088 Processor<T> make(A data, Type value); 089 090 @Override 091 default Processor<T> make(A data, Type value, AnnotatedElement element) { 092 return make(data, value); 093 } 094 } 095 096 /** 097 * Apply comments from {@link Comment} annotation on save. 098 * 099 * @return a new processor factory 100 * @since 4.0.0 101 */ 102 static Processor.Factory<Comment, Object> comments() { 103 return (data, fieldType) -> (value, destination) -> { 104 if (destination instanceof CommentedConfigurationNodeIntermediary<?>) { 105 final CommentedConfigurationNodeIntermediary<?> commented = (CommentedConfigurationNodeIntermediary<?>) destination; 106 if (data.override()) { 107 commented.comment(data.value()); 108 } else { 109 commented.commentIfAbsent(data.value()); 110 } 111 } 112 }; 113 } 114 115 /** 116 * Apply localized comments from {@link Comment} annotation on save. 117 * 118 * <p>The {@link Comment#value() comment's value} will be treated as a key 119 * into {@code source}, resolved to the system default locale. Missing keys 120 * will be written literally to node.</p> 121 * 122 * @param source source bundle for comments 123 * @return a new processor factory 124 * @since 4.0.0 125 */ 126 static Processor.Factory<Comment, Object> localizedComments(final ResourceBundle source) { 127 return (data, fieldType) -> { 128 final String translated = Localization.key(source, data.value()); 129 return (value, destination) -> { 130 if (destination instanceof CommentedConfigurationNodeIntermediary<?>) { 131 final CommentedConfigurationNodeIntermediary<?> commented = (CommentedConfigurationNodeIntermediary<?>) destination; 132 if (data.override()) { 133 commented.comment(translated); 134 } else { 135 commented.commentIfAbsent(translated); 136 } 137 } 138 }; 139 }; 140 } 141 142}