001package ball.tools.javadoc; 002/*- 003 * ########################################################################## 004 * Utilities 005 * $Id: InjectedFieldsTaglet.java 6035 2020-05-25 03:01:46Z ball $ 006 * $HeadURL: svn+ssh://svn.hcf.dev/var/spool/scm/repository.svn/ball-util/trunk/src/main/java/ball/tools/javadoc/InjectedFieldsTaglet.java $ 007 * %% 008 * Copyright (C) 2008 - 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 ball.annotation.ServiceProviderFor; 024import ball.xml.FluentNode; 025import com.sun.javadoc.ClassDoc; 026import com.sun.javadoc.Tag; 027import com.sun.tools.doclets.Taglet; 028import java.lang.annotation.Annotation; 029import java.lang.annotation.Retention; 030import java.lang.reflect.Field; 031import java.util.HashSet; 032import java.util.Map; 033import java.util.Set; 034import java.util.stream.Stream; 035import lombok.NoArgsConstructor; 036import lombok.ToString; 037 038import static org.apache.commons.lang3.StringUtils.isNotEmpty; 039 040/** 041 * Inline {@link Taglet} to provide a report of members whose values are 042 * injected. 043 * 044 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball} 045 * @version $Revision: 6035 $ 046 */ 047@ServiceProviderFor({ Taglet.class }) 048@TagletName("injected.fields") 049@NoArgsConstructor @ToString 050public class InjectedFieldsTaglet extends AbstractInlineTaglet 051 implements SunToolsInternalToolkitTaglet { 052 private static final InjectedFieldsTaglet INSTANCE = 053 new InjectedFieldsTaglet(); 054 055 public static void register(Map<Object,Object> map) { 056 register(map, INSTANCE); 057 } 058 059 private static final String[] NAMES = new String[] { 060 javax.annotation.Resource.class.getName(), 061 javax.annotation.Resources.class.getName(), 062 "javax.inject.Inject", 063 "javax.inject.Named", 064 "org.springframework.beans.factory.annotation.Autowired", 065 "org.springframework.beans.factory.annotation.Value" 066 }; 067 068 @Override 069 public FluentNode toNode(Tag tag) throws Throwable { 070 ClassDoc doc = null; 071 String[] argv = tag.text().trim().split("[\\p{Space}]+", 2); 072 073 if (isNotEmpty(argv[0])) { 074 doc = getClassDocFor(tag, argv[0]); 075 } else { 076 doc = containingClass(tag); 077 } 078 079 Set<Class<? extends Annotation>> set = new HashSet<>(); 080 081 for (String name : NAMES) { 082 Class<?> type = null; 083 084 try { 085 type = Class.forName(name); 086 } catch (Exception exception) { 087 } 088 089 if (type != null) { 090 Class<? extends Annotation> annotation = 091 type.asSubclass(Annotation.class); 092 Retention retention = 093 annotation.getAnnotation(Retention.class); 094 095 if (retention == null) { 096 throw new IllegalStateException(annotation.getCanonicalName() 097 + " does not specify a retention policy"); 098 } 099 100 switch (retention.value()) { 101 case RUNTIME: 102 break; 103 104 case CLASS: 105 case SOURCE: 106 default: 107 throw new IllegalStateException(annotation.getCanonicalName() 108 + " specifies " 109 + retention.value() 110 + " retention policy"); 111 /* break; */ 112 } 113 114 set.add(annotation); 115 } 116 } 117 118 if (set.isEmpty()) { 119 throw new IllegalStateException("No annotations to map"); 120 } 121 122 return div(attr("class", "summary"), 123 h3("Injected Field Summary"), 124 table(tag, getClassFor(doc), set)); 125 } 126 127 private FluentNode table(Tag tag, Class<?> type, 128 Set<Class<? extends Annotation>> set) { 129 return table(thead(tr(th("Annotation(s)"), th("Field"))), 130 tbody(Stream.of(type.getDeclaredFields()) 131 .filter(t -> (Stream.of(t.getAnnotations()) 132 .filter(a -> set.contains(a.annotationType())) 133 .findFirst().isPresent())) 134 .map(t -> tr(tag, t, set)))); 135 } 136 137 private FluentNode tr(Tag tag, Field field, 138 Set<Class<? extends Annotation>> set) { 139 return tr(td(fragment(Stream.of(field.getAnnotations()) 140 .filter(t -> set.contains(t.annotationType())) 141 .map(t -> annotation(tag, t)))), 142 td(declaration(tag, field))); 143 } 144}