001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community 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 * http://www.opensource.org/licenses/ecl2.php 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 */ 016package org.kuali.rice.krad.devtools.doclet; 017 018import com.sun.javadoc.AnnotationDesc; 019import com.sun.javadoc.ClassDoc; 020import com.sun.javadoc.MethodDoc; 021import com.sun.javadoc.ProgramElementDoc; 022import com.sun.javadoc.RootDoc; 023import com.sun.tools.javadoc.Main; 024 025import java.io.FileOutputStream; 026import java.io.IOException; 027import java.util.Arrays; 028import java.util.Collections; 029import java.util.Enumeration; 030import java.util.Properties; 031import java.util.Vector; 032 033/** 034 * A JavaDoc tools doclet class that generates a properties file containing information about 035 * any @BeanTag classes @BeanTagAttribute properties 036 * 037 * @author Kuali Rice Team (rice.collab@kuali.org) 038 */ 039public class KRADLibraryPropertiesDoclet { 040 041 // TODO : remove hard coded arguments 042 public static void main(String[] args) { 043 Main.execute(new String[]{"-doclet", "org.kuali.rice.krad.demo.uif.library.tools.KRADLibraryPropertiesDoclet", 044 "-sourcepath", "C:/Java/Projects/Rice/Trunk/krad/krad-web-framework/src/main/java", "-subpackages", 045 "org.kuali.rice.krad.uif:org.kuali.rice.krad.datadictionary.validation.constraint"}); 046 } 047 048 /* 049 * The method that will be called by the JavaDoc tool when executing this doclet 050 * 051 * @param root - the RootDoc containing all the class information as specified by the javadoc call arguments 052 * @return boolean 053 */ 054 public static boolean start(RootDoc root) { 055 storeToPropertyFile(root); 056 return true; 057 } 058 059 /* 060 * Writes all BeanTag properties to a propties file with their getter javadoc description 061 * 062 * @param root- the RootDoc containing all the class information as specified by the javadoc call arguments 063 */ 064 private static void storeToPropertyFile(RootDoc root) { 065 ClassDoc[] classes = root.classes(); 066 SortedProperties prop = new SortedProperties(); 067 068 for (ClassDoc classDoc : classes) { 069 070 // Check only BeanTag classes 071 if (isAnnotatedWith(classDoc, "org.kuali.rice.krad.datadictionary.parse.BeanTag", 072 "org.kuali.rice.krad.datadictionary.parse.BeanTags")) { 073 074 String className = classDoc.qualifiedName(); 075 String classDescription = classDoc.commentText(); 076 prop.setProperty(className, classDescription); 077 078 MethodDoc[] methods = classDoc.methods(); 079 080 for (MethodDoc methodDoc : methods) { 081 082 // Check only getters that i BeanTagAttribute annotated 083 if (methodDoc.parameters().length == 0 && 084 (methodDoc.name().startsWith("get") || methodDoc.name().startsWith("is")) && 085 !(methodDoc.name().equals("get") || methodDoc.name().equals("is")) && 086 isAnnotatedWith(methodDoc, "org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute")) { 087 String methodName; 088 089 if (methodDoc.name().startsWith("get")) { 090 methodName = methodDoc.name().replaceFirst("get", ""); 091 } else { 092 methodName = methodDoc.name().replaceFirst("is", ""); 093 } 094 String propertyName = Character.toLowerCase(methodName.charAt(0)) + (methodName.length() > 1 ? 095 methodName.substring(1) : ""); 096 String propertyType = methodDoc.returnType().typeName(); 097 String propertyDescription = getDocText(methodDoc, root); 098 099 prop.setProperty(className + "|" + propertyName + "|" + propertyType, propertyDescription); 100 } 101 } 102 } 103 } 104 105 // TODO : remove hard coding of filepath 106 try { 107 prop.store(new FileOutputStream("C:/Java/Projects/Rice/Trunk/sampleapp/src/main/resources/" 108 + "org/kuali/rice/devtools/krad/documentation/PropertiesDescription.properties"), null); 109 } catch (IOException e) { 110 e.printStackTrace(); 111 } 112 } 113 114 /* 115 * Returns the javadoc text for a method, when it is a @see comment the referenced methods documentation will 116 * be returned 117 * 118 * @param method 119 * @return String 120 */ 121 private static String getDocText(MethodDoc method, RootDoc root) { 122 123 if (method.commentText() != null && !method.commentText().equals("")) { 124 return method.commentText(); 125 } 126 127 String docText = method.getRawCommentText(); 128 129 if (docText != null && docText.contains("@see")) { 130 String classMethodName = docText.replace("@see", "").replace("\n", "").replace(" ", ""); 131 String className = classMethodName.substring(0, classMethodName.indexOf("#")); 132 String methodName = classMethodName.substring(classMethodName.indexOf("#") + 1).replace("()", ""); 133 ClassDoc classDoc = root.classNamed(className); 134 135 if (classDoc == null) { 136 System.err.println("warning - Comment on " 137 + method.toString() 138 + " does not have valid fully qualified " 139 + "method in @see annotation.\n" 140 + docText); 141 return ""; 142 } 143 MethodDoc methodDoc = getNoParamMethodFromClassDocByName(classDoc, methodName); 144 return methodDoc.commentText(); 145 } 146 147 return ""; 148 } 149 150 /* 151 * Finds the no arguments method's MethodDoc on a class with a specified name 152 * 153 * @param classDoc 154 * @param methodName 155 * @return MethodDoc 156 */ 157 private static MethodDoc getNoParamMethodFromClassDocByName(ClassDoc classDoc, String methodName) { 158 MethodDoc[] methods = classDoc.methods(); 159 160 for (MethodDoc methodDoc : methods) { 161 162 if (methodDoc.name().equals(methodName) && methodDoc.parameters().length == 0) { 163 return methodDoc; 164 } 165 } 166 return null; 167 } 168 169 /* 170 * Checks if a specific ClassDoc or MethodDoc is annotated with the specified annotations 171 * 172 * @param elementDoc 173 * @param tagString 174 * @return 175 */ 176 private static boolean isAnnotatedWith(ProgramElementDoc elementDoc, String... tagString) { 177 AnnotationDesc[] annotations = elementDoc.annotations(); 178 179 for (AnnotationDesc annotation : annotations) { 180 181 if (Arrays.asList(tagString).contains(annotation.annotationType().toString())) { 182 return true; 183 } 184 } 185 186 return false; 187 } 188 189 /* 190 * This class keeps properties sorted in order to make the file easier to read 191 */ 192 static class SortedProperties extends Properties { 193 194 public Enumeration keys() { 195 Enumeration keysEnum = super.keys(); 196 Vector<String> keyList = new Vector<String>(); 197 198 while (keysEnum.hasMoreElements()) { 199 keyList.add((String) keysEnum.nextElement()); 200 } 201 Collections.sort(keyList); 202 return keyList.elements(); 203 } 204 } 205 206}