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.serialize.SerializationException; 021 022import java.lang.annotation.Annotation; 023import java.lang.reflect.Type; 024import java.text.MessageFormat; 025import java.util.ResourceBundle; 026import java.util.regex.Matcher; 027import java.util.regex.Pattern; 028 029/** 030 * Perform a validation on data upon load. 031 * 032 * @param <V> value type 033 * @since 4.0.0 034 */ 035public interface Constraint<V> { 036 037 /** 038 * Check if the provided deserialized value matches an expected condition. 039 * 040 * @param value value to test 041 * @throws SerializationException if the value falls outside its constraint. 042 * @since 4.0.0 043 */ 044 void validate(@Nullable V value) throws SerializationException; 045 046 /** 047 * Provider for a specific constraint given a field type. 048 * 049 * @param <A> annotation type 050 * @param <V> data type 051 * @since 4.0.0 052 */ 053 interface Factory<A extends Annotation, V> { 054 055 /** 056 * Create a new specialized constraint. 057 * 058 * @param data annotation with metadata 059 * @param type annotated type. is a subtype of {@code V} 060 * @return new constraint 061 * @since 4.0.0 062 */ 063 Constraint<V> make(A data, Type type); 064 } 065 066 /** 067 * Require a value to be present for fields marked with the 068 * annotation {@code T}. 069 * 070 * @param <T> marker annotation type 071 * @return new constraint factory 072 * @since 4.0.0 073 */ 074 static <T extends Annotation> Constraint.Factory<T, Object> required() { 075 return (data, type) -> value -> { 076 if (value == null) { 077 throw new SerializationException("A value is required for this field"); 078 } 079 }; 080 } 081 082 /** 083 * Require values to match the {@link Matches#value() pattern} provided. 084 * 085 * <p>Upon failure, an error message will be taken from the annotation.</p> 086 * 087 * @return factory providing matching pattern test 088 * @since 4.0.0 089 */ 090 static Constraint.Factory<Matches, String> pattern() { 091 return (data, type) -> { 092 final Pattern test = Pattern.compile(data.value()); 093 final MessageFormat format = new MessageFormat(data.failureMessage()); 094 return value -> { 095 if (value != null) { 096 final Matcher match = test.matcher(value); 097 if (!match.matches()) { 098 throw new SerializationException(format.format(new Object[]{value, data.value()})); 099 } 100 } 101 }; 102 }; 103 } 104 105 /** 106 * Require values to match the {@link Matches#value() pattern} provided. 107 * 108 * <p>Upon failure, an error message will be taken from {@code bundle} with 109 * a key defined in the annotation.</p> 110 * 111 * @param bundle source for localized messages 112 * @return factory providing matching pattern test 113 * @since 4.0.0 114 */ 115 static Constraint.Factory<Matches, String> localizedPattern(final ResourceBundle bundle) { 116 return (data, type) -> { 117 final Pattern test = Pattern.compile(data.value()); 118 final MessageFormat format = new MessageFormat(Localization.key(bundle, data.failureMessage()), bundle.getLocale()); 119 return value -> { 120 if (value != null) { 121 final Matcher match = test.matcher(value); 122 if (!match.matches()) { 123 throw new SerializationException(format.format(new Object[]{value, data.value()})); 124 } 125 } 126 }; 127 }; 128 } 129 130}