001package com.mrivanplays.annotationconfig.core; 002 003import java.lang.annotation.Annotation; 004import java.lang.reflect.Field; 005import java.util.HashMap; 006import java.util.Map; 007import java.util.function.Supplier; 008 009/** Represents annotation registry for custom annotations. */ 010public final class CustomAnnotationRegistry { 011 012 /** 013 * Represents annotation resolver, responsible for........ wait for it....... resolving custom 014 * annotations!!! 015 * 016 * @param <T> type of the annotation resolved 017 */ 018 public interface AnnotationResolver<T extends Annotation> { 019 020 /** 021 * Called when a brand new config is being generated, should write the custom annotation's 022 * values to the writer with the desired syntax of the config type. There are syntax exceptions 023 * for config types the project maintains itself, and they are the following: 024 * 025 * <ul> 026 * <li>TOML: When writing something, it needs to be in the format <code>key=value</code>, or 027 * be a map. 028 * </ul> 029 * 030 * @param writer writer 031 * @param annotation annotation written 032 * @param context writer context 033 */ 034 void write(AnnotationWriter writer, T annotation, AnnotationResolverContext context); 035 036 /** 037 * Called when it is being read from the config, should return a {@link Supplier} of the 038 * annotation's {@link FieldTypeResolver}. 039 * 040 * @return field type resolver supplier 041 */ 042 Supplier<FieldTypeResolver> typeResolver(); 043 } 044 045 /** Represents a {@link AnnotationResolver} context. */ 046 public static final class AnnotationResolverContext { 047 048 private final Class<?> configType; 049 private final Field field; 050 private final Object annotatedConfig; 051 private final Object defaultsToValue; 052 private final String keyName; 053 private final boolean partOfConfigObject; 054 055 public AnnotationResolverContext( 056 Class<?> configType, 057 Field field, 058 Object annotatedConfig, 059 Object defaultsToValue, 060 String keyName, 061 boolean partOfConfigObject) { 062 this.configType = configType; 063 this.field = field; 064 this.annotatedConfig = annotatedConfig; 065 this.defaultsToValue = defaultsToValue; 066 this.keyName = keyName; 067 this.partOfConfigObject = partOfConfigObject; 068 } 069 070 /** 071 * Returns the config type, which triggered the write method. 072 * 073 * @return config type 074 */ 075 public Class<?> getConfigType() { 076 return configType; 077 } 078 079 /** 080 * Returns the field, holder of the written annotation. 081 * 082 * @return field 083 */ 084 public Field getField() { 085 return field; 086 } 087 088 /** 089 * Returns the annotated config, holder of the field. 090 * 091 * @return annotated config 092 */ 093 public Object getAnnotatedConfig() { 094 return annotatedConfig; 095 } 096 097 /** 098 * Returns the defaults value. 099 * 100 * @return defaults 101 */ 102 public Object getDefaultsToValue() { 103 return defaultsToValue; 104 } 105 106 /** 107 * Returns the preferred name of the field for configuration use. 108 * 109 * @return key 110 */ 111 public String getKeyName() { 112 return keyName; 113 } 114 115 /** 116 * Returns whether or not the field is a part of config object, and the {@link 117 * #getAnnotatedConfig()} is a config object. 118 * 119 * @return config object or not 120 */ 121 public boolean isPartOfConfigObject() { 122 return partOfConfigObject; 123 } 124 } 125 126 /** 127 * Represents a wrapped writer. 128 * 129 * <p>The reason behind why we don't use java's writer is because different file formats have 130 * different ways of writing things, and so we want to make advantage of that. 131 */ 132 public static final class AnnotationWriter { 133 134 private Map<WriteFunction, Object> toWrite = new HashMap<>(); 135 136 /** @deprecated internal use only */ 137 @Deprecated 138 public Map<WriteFunction, Object> toWrite() { 139 return toWrite; 140 } 141 142 /** 143 * Writes a string. 144 * 145 * @param s string 146 */ 147 public void write(String s) { 148 toWrite.put(WriteFunction.WRITE, s); 149 } 150 151 /** 152 * Writes a character array. This will probably be converted to string upon writing. 153 * 154 * @param chars character array 155 */ 156 public void write(char[] chars) { 157 toWrite.put(WriteFunction.WRITE, chars); 158 } 159 160 /** 161 * Writes a object. 162 * 163 * @param obj object 164 */ 165 public void write(Object obj) { 166 toWrite.put(WriteFunction.WRITE, obj); 167 } 168 169 /** 170 * Writes a single character. 171 * 172 * @param c character 173 */ 174 public void write(char c) { 175 toWrite.put(WriteFunction.WRITE, c); 176 } 177 178 /** 179 * Appends the typed character. 180 * 181 * @param c character 182 */ 183 public void append(char c) { 184 toWrite.put(WriteFunction.APPEND, c); 185 } 186 187 public enum WriteFunction { 188 APPEND, 189 WRITE 190 } 191 } 192 193 private Map<AnnotationType, AnnotationResolver<? extends Annotation>> registry = new HashMap<>(); 194 195 /** 196 * Registers a new annotation type. 197 * 198 * @param rawAnnoType raw annotation type 199 * @param annoWriter annotation resolver 200 * @param <T> type of annotation registered 201 */ 202 public <T extends Annotation> void register( 203 Class<T> rawAnnoType, AnnotationResolver<T> annoWriter) { 204 registry.put(new AnnotationType(rawAnnoType), annoWriter); 205 } 206 207 /** @deprecated internal use only */ 208 @Deprecated 209 public Map<AnnotationType, AnnotationResolver<? extends Annotation>> registry() { 210 return registry; 211 } 212}