001package ball.lang.reflect;
002/*-
003 * ##########################################################################
004 * Utilities
005 * %%
006 * Copyright (C) 2020 - 2022 Allen D. Ball
007 * %%
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *      http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 * ##########################################################################
020 */
021import java.lang.reflect.Constructor;
022import java.lang.reflect.Field;
023import java.lang.reflect.Member;
024import java.lang.reflect.Method;
025import java.lang.reflect.Parameter;
026import java.lang.reflect.ParameterizedType;
027import java.lang.reflect.Type;
028import java.util.EnumSet;
029import java.util.stream.Stream;
030import javax.lang.model.element.Modifier;
031
032import static java.util.stream.Collectors.joining;
033
034/**
035 * {@link java.lang.reflect} and {@link javax.lang.model.element} utility
036 * methods.
037 *
038 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball}
039 */
040public interface JavaLangReflectMethods {
041
042    /**
043     * Method to translate {@link Class} and {@link Member}
044     * {@link java.lang.reflect.Modifier} flags to an {@link EnumSet} of
045     * {@link Modifier}s.
046     *
047     * @param   modifiers       The {@code int} representing the modifiers.
048     *
049     * @return  The {@link EnumSet} of {@link Modifier}s.
050     */
051    default EnumSet<Modifier> asModifierSet(int modifiers) {
052        EnumSet<Modifier> set = EnumSet.noneOf(Modifier.class);
053
054        Stream.of(java.lang.reflect.Modifier.toString(modifiers).split("[\\p{Space}]+"))
055            .filter(t -> (! t.isEmpty()))
056            .map(String::toUpperCase)
057            .map(Modifier::valueOf)
058            .forEach(set::add);
059
060        return set;
061    }
062
063    /**
064     * See {@link #asModifierSet(int)}.
065     *
066     * @param   type            The {@link Class}.
067     *
068     * @return  The {@link EnumSet} of {@link Modifier}s.
069     */
070    default EnumSet<Modifier> getModifiers(Class<?> type) {
071        return asModifierSet(type.getModifiers());
072    }
073
074    /**
075     * See {@link #asModifierSet(int)}.
076     *
077     * @param   member          The {@link Member}.
078     *
079     * @return  The {@link EnumSet} of {@link Modifier}s.
080     */
081    default EnumSet<Modifier> getModifiers(Member member) {
082        return asModifierSet(member.getModifiers());
083    }
084
085    /**
086     * Dispatches call to {@link #declaration(Constructor)},
087     * {@link #declaration(Field)}, or {@link #declaration(Method)} as
088     * appropriate.
089     *
090     * @param   member          The target {@link Member}.
091     *
092     * @return  {@link String}
093     */
094    default String declaration(Member member) {
095        String string = null;
096
097        if (member instanceof Constructor) {
098            string = declaration((Constructor) member);
099        } else if (member instanceof Field) {
100            string = declaration((Field) member);
101        } else if (member instanceof Method) {
102            string = declaration((Method) member);
103        } else {
104            throw new IllegalArgumentException(String.valueOf(member));
105        }
106
107        return string;
108    }
109
110    /**
111     * Method to generate a {@link Constructor} declaration.
112     *
113     * @param   constructor     The target {@link Constructor}.
114     *
115     * @return  {@link String}
116     */
117    default String declaration(Constructor<?> constructor) {
118        String string =
119            modifiers(constructor.getModifiers())
120            + " " + constructor.getName()
121            + parameters(constructor.getParameters())
122            + " " + exceptions(constructor.getGenericExceptionTypes());
123
124        return string.trim();
125    }
126
127    /**
128     * Method to generate a {@link Field} declaration.
129     *
130     * @param   field           The target {@link Field}.
131     *
132     * @return  {@link String}
133     */
134    default String declaration(Field field) {
135        String string = modifiers(field.getModifiers()) + " " + type(field.getGenericType()) + " " + field.getName();
136
137        return string.trim();
138    }
139
140    /**
141     * Method to generate a {@link Method} declaration.
142     *
143     * @param   method          The target {@link Method}.
144     *
145     * @return  {@link String}
146     */
147    default String declaration(Method method) {
148        return declaration(method.getModifiers(), method);
149    }
150
151    /**
152     * Method to generate a {@link Method} declaration.
153     *
154     * @param   modifiers       The adjusted modifiers.
155     * @param   method          The target {@link Method}.
156     *
157     * @return  {@link String}
158     */
159    default String declaration(int modifiers, Method method) {
160        return declaration(modifiers, method.getGenericReturnType(), method);
161    }
162
163    /**
164     * Method to generate a {@link Method} declaration.
165     *
166     * @param   modifiers       The adjusted modifiers.
167     * @param   returnType      The adjusted return {@link Type}.
168     * @param   method          The target {@link Method}.
169     *
170     * @return  {@link String}
171     */
172    default String declaration(int modifiers, Type returnType, Method method) {
173        return declaration(modifiers, type(returnType), method);
174    }
175
176    /**
177     * Method to generate a {@link Method} declaration.
178     *
179     * @param   modifiers       The adjusted modifiers.
180     * @param   returnType      The adjusted return {@link Type}
181     *                          (as a {@link String}).
182     * @param   method          The target {@link Method}.
183     *
184     * @return  {@link String}
185     */
186    default String declaration(int modifiers, String returnType, Method method) {
187        String string =
188            modifiers(modifiers)
189            + " " + returnType
190            + " " + method.getName()
191            + parameters(method.getParameters())
192            + " " + exceptions(method.getGenericExceptionTypes());
193
194        return string.trim();
195    }
196
197    /**
198     * Method to generate a {@link Constructor} or {@link Method} parameter
199     * declaration.
200     *
201     * @param   parameters      The target {@link Parameter} array.
202     *
203     * @return  {@link String}
204     */
205    default String parameters(Parameter[] parameters) {
206        return Stream.of(parameters)
207               .map(t -> declaration(t))
208               .collect(joining(", ", "(", ")"));
209    }
210
211    /**
212     * Method to generate a {@link Constructor} or {@link Method} thrown
213     * exception list.
214     *
215     * @param   exceptions      The target {@link Type} array.
216     *
217     * @return  {@link String}
218     */
219    default String exceptions(Type[] exceptions) {
220        String string = "";
221
222        if (exceptions != null && exceptions.length > 0) {
223            string =
224                Stream.of(exceptions)
225                .map(t -> type(t))
226                .collect(joining(", ", "throws ", ""));
227        }
228
229        return string;
230    }
231
232    /**
233     * Method to generate a {@link Parameter} declaration.
234     *
235     * @param   parameter       The target {@link Parameter}.
236     *
237     * @return  {@link String}
238     */
239    default String declaration(Parameter parameter) {
240        String string =
241            modifiers(parameter.getModifiers())
242            + " " + type(parameter.getParameterizedType())
243            + " " + parameter.getName();
244
245        return string.trim();
246    }
247
248    /**
249     * Method to generate modifiers for {@code declaration()} methods.
250     *
251     * @param   modifiers       See {@link Modifier}.
252     *
253     * @return  {@link String}
254     */
255    default String modifiers(int modifiers) {
256        return java.lang.reflect.Modifier.toString(modifiers);
257    }
258
259    /**
260     * Method to generate types for {@code declaration()} methods.
261     *
262     * @param   type            The {@link Type}.
263     *
264     * @return  {@link String}
265     */
266    default String type(Type type) {
267        String string = null;
268
269        if (type instanceof ParameterizedType) {
270            string = type((ParameterizedType) type);
271        } else if (type instanceof Class<?>) {
272            string = ((Class<?>) type).getSimpleName();
273        } else {
274            string = type.getTypeName();
275        }
276
277        return string;
278    }
279
280    /**
281     * Method to generate types for {@code declaration()} methods.
282     *
283     * @param   type            The {@link ParameterizedType}.
284     *
285     * @return  {@link String}
286     */
287    default String type(ParameterizedType type) {
288        return Stream.of(type.getActualTypeArguments())
289               .map(t -> type(t))
290               .collect(joining(",", type(type.getRawType()) + "<", ">"));
291    }
292}