001/*
002 * Licensed under the Apache License, Version 2.0 (the "License");
003 * you may not use this file except in compliance with the License.
004 * You may obtain a copy of the License at
005 *
006 *      http://www.apache.org/licenses/LICENSE-2.0
007 *
008 * Unless required by applicable law or agreed to in writing, software
009 * distributed under the License is distributed on an "AS IS" BASIS,
010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011 * See the License for the specific language governing permissions and
012 * limitations under the License.
013 */
014package org.apache.shiro.cdi;
015
016import java.lang.annotation.Annotation;
017import java.util.Set;
018import java.util.stream.Collectors;
019import java.util.stream.Stream;
020import javax.enterprise.inject.spi.AnnotatedType;
021
022import lombok.Getter;
023import lombok.experimental.Delegate;
024
025/**
026 * Wraps annotation types to facilitate additional annotations for CDI
027 *
028 * @param <T> type of annotated class
029 */
030public class AnnotatedTypeWrapper<T> implements AnnotatedType<T> {
031    // the below is so the compiler doesn't complain about unchecked casts
032    private abstract class AT implements AnnotatedType<T> {
033    }
034
035    private final @Delegate(types = AT.class) AnnotatedType<T> wrapped;
036    private final @Getter Set<Annotation> annotations;
037
038
039    public AnnotatedTypeWrapper(AnnotatedType<T> wrapped, Annotation... additionalAnnotations) {
040        this(wrapped, true, Set.of(additionalAnnotations), Set.of());
041    }
042
043
044    public AnnotatedTypeWrapper(AnnotatedType<T> wrapped, boolean keepOriginalAnnotations,
045                                Set<Annotation> additionalAnnotations, Set<Annotation> annotationsToRemove) {
046        this.wrapped = wrapped;
047        Stream.Builder<Annotation> builder = Stream.builder();
048        if (keepOriginalAnnotations) {
049            var annotationTypesToExclude = annotationsToRemove.stream()
050                    .map(AnnotatedTypeWrapper::checkIfAnnotation)
051                    .map(Annotation::annotationType).collect(Collectors.toSet());
052            wrapped.getAnnotations().stream().filter(ann ->
053                            !annotationTypesToExclude.contains(ann.annotationType()))
054                    .forEach(builder::add);
055        }
056        additionalAnnotations.forEach(annotation -> addToBuilder(builder, annotation));
057        annotations = builder.build().collect(Collectors.toSet());
058    }
059
060    @Override
061    public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
062        return annotations.stream().anyMatch(annotationType::isInstance);
063    }
064
065    private void addToBuilder(Stream.Builder<Annotation> builder, Annotation ann) {
066        checkIfAnnotation(ann);
067        builder.add(ann);
068    }
069
070    private static Annotation checkIfAnnotation(Annotation ann) {
071        if (!ann.annotationType().isInstance(ann)) {
072            throw new IllegalArgumentException(ann.getClass().getName());
073        }
074        return ann;
075    }
076}