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