001/* 002 * Copyright 2002-2020 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.core.annotation; 018 019import java.lang.annotation.Annotation; 020import java.util.ArrayList; 021import java.util.Collection; 022import java.util.LinkedHashSet; 023import java.util.Set; 024import java.util.function.Function; 025import java.util.function.IntFunction; 026import java.util.stream.Collector; 027import java.util.stream.Collector.Characteristics; 028 029import org.springframework.core.annotation.MergedAnnotation.Adapt; 030import org.springframework.util.LinkedMultiValueMap; 031import org.springframework.util.MultiValueMap; 032 033/** 034 * {@link Collector} implementations that provide various reduction operations for 035 * {@link MergedAnnotation} instances. 036 * 037 * @author Phillip Webb 038 * @author Sam Brannen 039 * @since 5.2 040 */ 041public abstract class MergedAnnotationCollectors { 042 043 private static final Characteristics[] NO_CHARACTERISTICS = {}; 044 045 private static final Characteristics[] IDENTITY_FINISH_CHARACTERISTICS = {Characteristics.IDENTITY_FINISH}; 046 047 048 private MergedAnnotationCollectors() { 049 } 050 051 052 /** 053 * Create a new {@link Collector} that accumulates merged annotations to a 054 * {@link LinkedHashSet} containing {@linkplain MergedAnnotation#synthesize() 055 * synthesized} versions. 056 * <p>The collector returned by this method is effectively equivalent to 057 * {@code Collectors.mapping(MergedAnnotation::synthesize, Collectors.toCollection(LinkedHashSet::new))} 058 * but avoids the creation of a composite collector. 059 * @param <A> the annotation type 060 * @return a {@link Collector} which collects and synthesizes the 061 * annotations into a {@link Set} 062 */ 063 public static <A extends Annotation> Collector<MergedAnnotation<A>, ?, Set<A>> toAnnotationSet() { 064 return Collector.of(LinkedHashSet::new, (set, annotation) -> set.add(annotation.synthesize()), 065 MergedAnnotationCollectors::combiner); 066 } 067 068 /** 069 * Create a new {@link Collector} that accumulates merged annotations to an 070 * {@link Annotation} array containing {@linkplain MergedAnnotation#synthesize() 071 * synthesized} versions. 072 * @param <A> the annotation type 073 * @return a {@link Collector} which collects and synthesizes the 074 * annotations into an {@code Annotation[]} 075 * @see #toAnnotationArray(IntFunction) 076 */ 077 public static <A extends Annotation> Collector<MergedAnnotation<A>, ?, Annotation[]> toAnnotationArray() { 078 return toAnnotationArray(Annotation[]::new); 079 } 080 081 /** 082 * Create a new {@link Collector} that accumulates merged annotations to an 083 * {@link Annotation} array containing {@linkplain MergedAnnotation#synthesize() 084 * synthesized} versions. 085 * @param <A> the annotation type 086 * @param <R> the resulting array type 087 * @param generator a function which produces a new array of the desired 088 * type and the provided length 089 * @return a {@link Collector} which collects and synthesizes the 090 * annotations into an annotation array 091 * @see #toAnnotationArray 092 */ 093 public static <R extends Annotation, A extends R> Collector<MergedAnnotation<A>, ?, R[]> toAnnotationArray( 094 IntFunction<R[]> generator) { 095 096 return Collector.of(ArrayList::new, (list, annotation) -> list.add(annotation.synthesize()), 097 MergedAnnotationCollectors::combiner, list -> list.toArray(generator.apply(list.size()))); 098 } 099 100 /** 101 * Create a new {@link Collector} that accumulates merged annotations to a 102 * {@link MultiValueMap} with items {@linkplain MultiValueMap#add(Object, Object) 103 * added} from each merged annotation 104 * {@linkplain MergedAnnotation#asMap(Adapt...) as a map}. 105 * @param <A> the annotation type 106 * @param adaptations the adaptations that should be applied to the annotation values 107 * @return a {@link Collector} which collects and synthesizes the 108 * annotations into a {@link LinkedMultiValueMap} 109 * @see #toMultiValueMap(Function, MergedAnnotation.Adapt...) 110 */ 111 public static <A extends Annotation> Collector<MergedAnnotation<A>, ?, MultiValueMap<String, Object>> toMultiValueMap( 112 Adapt... adaptations) { 113 114 return toMultiValueMap(Function.identity(), adaptations); 115 } 116 117 /** 118 * Create a new {@link Collector} that accumulates merged annotations to a 119 * {@link MultiValueMap} with items {@linkplain MultiValueMap#add(Object, Object) 120 * added} from each merged annotation 121 * {@linkplain MergedAnnotation#asMap(Adapt...) as a map}. 122 * @param <A> the annotation type 123 * @param finisher the finisher function for the new {@link MultiValueMap} 124 * @param adaptations the adaptations that should be applied to the annotation values 125 * @return a {@link Collector} which collects and synthesizes the 126 * annotations into a {@link LinkedMultiValueMap} 127 * @see #toMultiValueMap(MergedAnnotation.Adapt...) 128 */ 129 public static <A extends Annotation> Collector<MergedAnnotation<A>, ?, MultiValueMap<String, Object>> toMultiValueMap( 130 Function<MultiValueMap<String, Object>, MultiValueMap<String, Object>> finisher, 131 Adapt... adaptations) { 132 133 Characteristics[] characteristics = (isSameInstance(finisher, Function.identity()) ? 134 IDENTITY_FINISH_CHARACTERISTICS : NO_CHARACTERISTICS); 135 return Collector.of(LinkedMultiValueMap::new, 136 (map, annotation) -> annotation.asMap(adaptations).forEach(map::add), 137 MergedAnnotationCollectors::combiner, finisher, characteristics); 138 } 139 140 141 private static boolean isSameInstance(Object instance, Object candidate) { 142 return instance == candidate; 143 } 144 145 /** 146 * {@link Collector#combiner() Combiner} for collections. 147 * <p>This method is only invoked if the {@link java.util.stream.Stream} is 148 * processed in {@linkplain java.util.stream.Stream#parallel() parallel}. 149 */ 150 private static <E, C extends Collection<E>> C combiner(C collection, C additions) { 151 collection.addAll(additions); 152 return collection; 153 } 154 155 /** 156 * {@link Collector#combiner() Combiner} for multi-value maps. 157 * <p>This method is only invoked if the {@link java.util.stream.Stream} is 158 * processed in {@linkplain java.util.stream.Stream#parallel() parallel}. 159 */ 160 private static <K, V> MultiValueMap<K, V> combiner(MultiValueMap<K, V> map, MultiValueMap<K, V> additions) { 161 map.addAll(additions); 162 return map; 163 } 164 165}