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}