001package ball.lang.reflect;
002/*-
003 * ##########################################################################
004 * Utilities
005 * $Id: JavaLangReflectMethods.java 5895 2020-05-08 15:55:11Z 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 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: 5895 $
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        String string =
168            modifiers(modifiers)
169            + " " + type(method.getGenericReturnType())
170            + " " + method.getName()
171            + parameters(method.getParameters())
172            + " " + exceptions(method.getGenericExceptionTypes());
173
174        return string.trim();
175    }
176
177    /**
178     * Method to generate a {@link Constructor} or {@link Method} parameter
179     * declaration.
180     *
181     * @param   parameters      The target {@link Parameter} array.
182     *
183     * @return  {@link String}
184     */
185    default String parameters(Parameter[] parameters) {
186        return Stream.of(parameters)
187               .map(t -> declaration(t))
188               .collect(joining(", ", "(", ")"));
189    }
190
191    /**
192     * Method to generate a {@link Constructor} or {@link Method} thrown
193     * exception list.
194     *
195     * @param   exceptions      The target {@link Type} array.
196     *
197     * @return  {@link String}
198     */
199    default String exceptions(Type[] exceptions) {
200        String string = "";
201
202        if (exceptions != null && exceptions.length > 0) {
203            string =
204                Stream.of(exceptions)
205                .map(t -> type(t))
206                .collect(joining(", ", "throws ", ""));
207        }
208
209        return string;
210    }
211
212    /**
213     * Method to generate a {@link Parameter} declaration.
214     *
215     * @param   parameter       The target {@link Parameter}.
216     *
217     * @return  {@link String}
218     */
219    default String declaration(Parameter parameter) {
220        String string =
221            modifiers(parameter.getModifiers())
222            + " " + type(parameter.getParameterizedType())
223            + " " + parameter.getName();
224
225        return string.trim();
226    }
227
228    /**
229     * Method to generate modifiers for {@code declaration()} methods.
230     *
231     * @param   modifiers       See {@link Modifier}.
232     *
233     * @return  {@link String}
234     */
235    default String modifiers(int modifiers) {
236        return java.lang.reflect.Modifier.toString(modifiers);
237    }
238
239    /**
240     * Method to generate types for {@code declaration()} methods.
241     *
242     * @param   type            The {@link Type}.
243     *
244     * @return  {@link String}
245     */
246    default String type(Type type) {
247        String string = null;
248
249        if (type instanceof ParameterizedType) {
250            string = type((ParameterizedType) type);
251        } else if (type instanceof Class<?>) {
252            string = ((Class<?>) type).getSimpleName();
253        } else {
254            string = type.getTypeName();
255        }
256
257        return string;
258    }
259
260    /**
261     * Method to generate types for {@code declaration()} methods.
262     *
263     * @param   type            The {@link ParameterizedType}.
264     *
265     * @return  {@link String}
266     */
267    default String type(ParameterizedType type) {
268        return Stream.of(type.getActualTypeArguments())
269               .map(t -> type(t))
270               .collect(joining(",", type(type.getRawType()) + "<", ">"));
271    }
272}