001/*
002 * ============================================================================
003 *  Copyright © 2002-2023 by Thomas Thrien.
004 *  All Rights Reserved.
005 * ============================================================================
006 *  Licensed to the public under the agreements of the GNU Lesser General Public
007 *  License, version 3.0 (the "License"). You may obtain a copy of the License at
008 *
009 *       http://www.gnu.org/licenses/lgpl.html
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, WITHOUT
013 *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
014 *  License for the specific language governing permissions and limitations
015 *  under the License.
016 */
017
018package org.tquadrat.foundation.ap;
019
020import static java.lang.reflect.Proxy.isProxyClass;
021import static org.apiguardian.api.API.Status.STABLE;
022import static org.tquadrat.foundation.lang.CommonConstants.EMPTY_STRING;
023import static org.tquadrat.foundation.lang.DebugOutput.ifDebug;
024import static org.tquadrat.foundation.lang.Objects.requireNonNullArgument;
025
026import javax.annotation.processing.Messager;
027import javax.annotation.processing.ProcessingEnvironment;
028import javax.lang.model.element.AnnotationMirror;
029import javax.lang.model.element.AnnotationValue;
030import javax.lang.model.element.Element;
031import javax.lang.model.element.ExecutableElement;
032import javax.lang.model.element.Name;
033import javax.lang.model.element.TypeElement;
034import javax.lang.model.type.TypeMirror;
035import javax.tools.Diagnostic;
036import java.lang.annotation.Annotation;
037import java.util.List;
038import java.util.NoSuchElementException;
039import java.util.Optional;
040import java.util.Set;
041import java.util.StringJoiner;
042
043import org.apiguardian.api.API;
044import org.tquadrat.foundation.annotation.ClassVersion;
045
046/**
047 *  The specification for a set of helpers for annotation processing.
048 *
049 *  @extauthor Thomas Thrien - thomas.thrien@tquadrat.org
050 *  @version $Id: APHelper.java 1061 2023-09-25 16:32:43Z tquadrat $
051 *  @since 0.1.0
052 *
053 *  @UMLGraph.link
054 */
055@ClassVersion( sourceVersion = "$Id: APHelper.java 1061 2023-09-25 16:32:43Z tquadrat $" )
056@API( status = STABLE, since = "0.1.0" )
057public interface APHelper extends Messager, ProcessingEnvironment
058{
059        /*---------*\
060    ====** Methods **==========================================================
061        \*---------*/
062    /**
063     *  <p>{@summary Returns the flag that indicates whether some debug
064     *  information should be emitted to the generated code.}</p>
065     *  <p>The value is controlled by the value of the annotation processor
066     *  option
067     *  {@value APBase#ADD_DEBUG_OUTPUT}.</p>
068     *
069     *  @return {@code true} if the debug information should be added,
070     *      {@code false} if not.
071     */
072    @SuppressWarnings( "BooleanMethodNameMustStartWithQuestion" )
073    public boolean addDebugOutput();
074
075    /**
076     *  Retrieves the
077     *  {@link AnnotationMirror}
078     *  for the given annotation
079     *  from the given
080     *  {@link Element}
081     *  instance. Only declared annotations are considered, not the inherited
082     *  ones.
083     *
084     *  @param  element The element.
085     *  @param  annotationClass The annotation to look for.
086     *  @return An instance of
087     *      {@link Optional}
088     *      that holds the found {@code AnnotationMirror} instance.
089     *
090     *  @see Element#getAnnotationMirrors()
091     */
092    public default Optional<? extends AnnotationMirror> getAnnotationMirror( final Element element, final Class<? extends Annotation> annotationClass )
093    {
094        final var isProxyClass = isProxyClass( requireNonNullArgument( annotationClass, "annotationClass" ) );
095        ifDebug( a -> "annotationClass = %s".formatted( ((Class<?>) a [0]).getName() ), annotationClass );
096        final String annotationClassName;
097        if( isProxyClass )
098        {
099            final var interfaces = annotationClass.getInterfaces();
100            ifDebug( interfaces.length > 1, currentInterfaces ->
101            {
102                final var joiner = new StringJoiner( ", ", "annotation interface: ", EMPTY_STRING );
103                //noinspection SuspiciousArrayCast
104                for( final var currentInterface : ((Class<?> []) currentInterfaces) )
105                {
106                    joiner.add( currentInterface.getName() );
107                }
108                return joiner.toString();
109            }, (Object []) interfaces );
110            if( interfaces.length != 1 )
111                throw new AnnotationProcessingError( "annotationClass proxy '%s' implements more than one interface".formatted( annotationClass.getName() ) );
112            annotationClassName = interfaces [0].getName();
113        }
114        else
115        {
116            annotationClassName = requireNonNullArgument( annotationClass, "annotationClass" ).getName();
117        }
118        ifDebug( "effective annotationClassName = %s"::formatted, annotationClassName );
119        final var retValue = requireNonNullArgument( element, "element" ).getAnnotationMirrors().stream()
120            .filter( annotationMirror -> annotationMirror.getAnnotationType().toString().equals( annotationClassName ) )
121            .findFirst();
122        ifDebug( retValue.isEmpty(), e ->
123        {
124            final var joiner = new StringJoiner( ", ", "annotationMirrors: ", EMPTY_STRING );
125            for( final var mirror : ((Element) e [0]).getAnnotationMirrors() )
126            {
127                joiner.add( mirror.getAnnotationType().toString() );
128            }
129            return joiner.toString();
130        }, element );
131
132        //---* Done *----------------------------------------------------------
133        return retValue;
134    }   //  getAnnotationMirror()
135
136    /**
137     *  Retrieves the annotation value from the given annotation mirror.
138     *
139     *  @param  annotationMirror    The annotation mirror.
140     *  @return An instance of
141     *      {@link Optional}
142     *      that holds the found annotation value.
143     */
144    public default Optional<? extends AnnotationValue> getAnnotationValue( final AnnotationMirror annotationMirror )
145    {
146        final var retValue = getAnnotationValue( annotationMirror, "value" );
147
148        //---* Done *----------------------------------------------------------
149        return retValue;
150    }   //  getAnnotationValue()
151
152    /**
153     *  Retrieves the annotation value with the given name from the given
154     *  annotation mirror.
155     *
156     *  @param  annotationMirror    The annotation mirror.
157     *  @param  name    The name of the desired value.
158     *  @return An instance of
159     *      {@link Optional}
160     *      that holds the found annotation value.
161     */
162    public default Optional<? extends AnnotationValue> getAnnotationValue( final AnnotationMirror annotationMirror, final String name )
163    {
164        final var elementValues = getElementUtils().getElementValuesWithDefaults( annotationMirror );
165        @SuppressWarnings( "unlikely-arg-type" )
166        final var retValue = elementValues.keySet().stream()
167            .filter( executableElement -> executableElement.getSimpleName().toString().equals( name ) )
168            .map( elementValues::get )
169            .findAny();
170
171        //---* Done *----------------------------------------------------------
172        return retValue;
173    }   //  getAnnotationValue()
174
175    /**
176     *  <p>{@summary Retrieves a
177     *  {@link TypeMirror}
178     *  instance from an annotation.}</p>
179     *  <p>In case an annotation defines a
180     *  {@link Class Class&lt;?&gt;}
181     *  attribute, the value for that attribute is either {@code null} or
182     *  something strange, but never an instance of {@code Class<?>}. So
183     *  we need some special code to get something useful from the
184     *  annotation.</p>
185     *  <p>This implementations assumes the default name
186     *  &quot;{@code value}&quot; for the attribute.</p>
187     *
188     *  @param  element The annotated element.
189     *  @param  annotationClass The type of the annotation.
190     *  @return An instance of
191     *      {@link Optional}
192     *      that holds the {@code TypeMirror}
193     *      instance.
194     *  @throws NoSuchElementException  The given element is not annotated with
195     *      an annotation of the given type.
196     */
197    public default Optional<TypeMirror> getTypeMirrorFromAnnotationValue( final Element element, final Class<? extends Annotation> annotationClass )
198    {
199        final var annotationMirror = getAnnotationMirror( element, annotationClass )
200            .orElseThrow( () -> new NoSuchElementException( annotationClass.getName() ) );
201
202        final var retValue = getAnnotationValue( annotationMirror )
203            .map( annotationValue -> (TypeMirror) annotationValue.getValue() );
204
205        //---* Done *----------------------------------------------------------
206        return retValue;
207    }   //  getTypeMirrorFromAnnotationValue()
208
209    /**
210     *  <p>{@summary Retrieves a
211     *  {@link TypeMirror}
212     *  instance from an annotation.}</p>
213     *  <p>In case an annotation defines a
214     *  {@link Class Class&lt;?&gt;}
215     *  attribute, the value for that attribute is either {@code null} or
216     *  something strange, but never an instance of {@code Class<?>}. So we
217     *  need some special code to get something useful from the annotation.</p>
218     *
219     *  @param  element The annotated element.
220     *  @param  annotationClass The type of the annotation.
221     *  @param  attributeName   The name of the attribute that holds the class.
222     *  @return An instance of
223     *      {@link Optional}
224     *      that holds the {@code TypeMirror}
225     *      instance.
226     *  @throws NoSuchElementException  The given element is not annotated with
227     *      an annotation of the given type.
228     */
229    public default Optional<TypeMirror> getTypeMirrorFromAnnotationValue( final Element element, final Class<? extends Annotation> annotationClass, final String attributeName )
230    {
231        final var annotationMirror = getAnnotationMirror( element, annotationClass )
232            .orElseThrow( () -> new NoSuchElementException( annotationClass.getName() ) );
233
234        final var retValue = getAnnotationValue( annotationMirror, attributeName )
235            .map( annotationValue -> (TypeMirror) annotationValue.getValue() );
236
237        //---* Done *----------------------------------------------------------
238        return retValue;
239    }   //  getTypeMirrorFromAnnotationValue()
240
241    /**
242     *  Checks whether the given element has the given annotation.
243     *
244     *  @note If the retention for the given annotation is not
245     *      {@link java.lang.annotation.RetentionPolicy#RUNTIME RUNTIME}
246     *      it could have been removed from the element in an earlier compile
247     *      run.
248     *
249     *  @param  <A> The type of the annotation.
250     *  @param  element The element to inspect.
251     *  @param  annotationType  The type of the annotation to look for.
252     *  @return {@code true} if the element is annotated with the given
253     *      annotation, {@code false} if not.
254     *
255     *  @see java.lang.annotation.RetentionPolicy#RUNTIME
256     */
257    public default <A extends Annotation> boolean hasAnnotation( final Element element, final Class<A> annotationType )
258    {
259        final var annotation = requireNonNullArgument( element, "element" ).getAnnotation( requireNonNullArgument( annotationType, "annotationType" ) );
260        final var retValue = annotation != null;
261
262        //---* Done *----------------------------------------------------------
263        return retValue;
264    }   //  hasAnnotation()
265
266    /**
267     *  Determines whether the given instance of
268     *  {@link TypeMirror}
269     *  is for an
270     *  {@link Enum}
271     *  type.
272     *
273     *  @param  type    The type to check.
274     *  @return {@code true} if the given type is an {@code Enum} type,
275     *      {@code false} otherwise.
276     */
277    public boolean isEnumType( final TypeMirror type );
278
279    /**
280     *  {@inheritDoc}
281     */
282    @Override
283    public default void printMessage( final Diagnostic.Kind kind, final CharSequence msg )
284    {
285        getMessager().printMessage( kind, msg );
286    }   //  printMessage()
287
288    /**
289     *  {@inheritDoc}
290     */
291    @Override
292    public default void printMessage( final Diagnostic.Kind kind, final CharSequence msg, final Element element )
293    {
294        getMessager().printMessage( kind, msg, element );
295    }   //  printMessage()
296
297    /**
298     *  {@inheritDoc}
299     */
300    @Override
301    public default void printMessage( final Diagnostic.Kind kind, final CharSequence msg, final Element element, final AnnotationMirror annotation )
302    {
303        getMessager().printMessage( kind, msg, element, annotation );
304    }   //  printMessage()
305
306    /**
307     *  {@inheritDoc}
308     */
309    @Override
310    public default void printMessage( final Diagnostic.Kind kind, final CharSequence msg, final Element element, final AnnotationMirror annotation, final AnnotationValue value )
311    {
312        getMessager().printMessage( kind, msg, element, annotation, value );
313    }   //  printMessage()
314
315    /**
316     *  <p>{@summary Retrieves the names of a method's arguments.}</p>
317     *  <p>This method will return the names of the arguments as defined only
318     *  if the compiler flag {@code -parameters} is set; otherwise, the
319     *  arguments are just counted ({@code arg0}, {@code arg1}, {@code arg2},
320     *  …).</p>
321     *
322     *  @param  method  The method.
323     *  @return The names of the arguments.
324     */
325    public List<Name> retrieveArgumentNames( final ExecutableElement method );
326
327    /**
328     *  <p>{@summary Retrieves the generic types for the given type.}</p>
329     *
330     *  @param  type    The
331     *      {@link TypeMirror} instance to inspect.
332     *  @return The type arguments the given type was defined with; will be the
333     *      empty list if that type was not generic.
334     */
335    public List<? extends TypeMirror> retrieveGenericTypes( final TypeMirror type );
336
337    /**
338     *  Retrieves the interfaces are that implemented or extended by the given
339     *  type element.
340     *
341     *  @param  typeElement The type element to inspect.
342     *  @param  interfaces  The already retrieved interfaces.
343     */
344    public default void retrieveInterfaces( final TypeElement typeElement, final Set<? super TypeElement> interfaces )
345    {
346        interfaces.add( typeElement );
347
348        for( final var typeMirror : typeElement.getInterfaces() )
349        {
350            final var nextInterface = (TypeElement) getTypeUtils().asElement( typeMirror );
351            if( !interfaces.contains( nextInterface ) )
352            {
353                retrieveInterfaces( nextInterface, interfaces );
354            }
355        }
356    }   //  retrieveInterfaces()
357}
358//  class APHelper
359
360/*
361 *  End of File
362 */