001/* 002 * Copyright (c) 2009-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: crawl $' 006 * '$Date: 2015-08-24 22:45:41 +0000 (Mon, 24 Aug 2015) $' 007 * '$Revision: 33631 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030package org.kepler.kar; 031 032import java.util.HashMap; 033import java.util.HashSet; 034import java.util.Map; 035import java.util.Set; 036 037import com.sun.javadoc.ClassDoc; 038import com.sun.javadoc.Doc; 039import com.sun.javadoc.FieldDoc; 040import com.sun.javadoc.RootDoc; 041import com.sun.javadoc.Tag; 042import com.sun.javadoc.Type; 043 044import ptolemy.kernel.util.IllegalActionException; 045import ptolemy.kernel.util.NameDuplicationException; 046import ptolemy.kernel.util.Workspace; 047import ptolemy.vergil.basic.KeplerDocumentationAttribute; 048 049/** 050 * A doclet that creates documentation for KAR files. 051 * 052 * @author Daniel Crawl 053 * @version $Id: KarDoclet.java 33631 2015-08-24 22:45:41Z crawl $ 054 */ 055 056public class KarDoclet { 057 /** The entry point called from javadoc. */ 058 public static boolean start(RootDoc root) { 059 _table.clear(); 060 061 try { 062 ClassDoc[] classes = root.classes(); 063 for (int i = 0; i < classes.length; i++) { 064 // make sure this is not deprecated 065 if(classes[i].tags("@deprecated").length > 0) { 066 if(!_generateForDeprecated) { 067 System.out.println("WARNING: skipping " + classes[i].qualifiedName() + " because it is deprecated."); 068 continue; 069 } 070 _deprecated.add(classes[i].qualifiedName()); 071 } 072 073 // javadoc returns inner classes in RootDoc.classes() 074 // make sure the current class is not an inner class 075 // NOTE: this assumes we've already parsed the parent 076 // class and put it in _table. 077 String name = classes[i].qualifiedName(); 078 name = name.substring(0, name.lastIndexOf(".")); 079 if (_table.get(name) == null) { 080 // _out("going to parse class: " + classes[i]); 081 _parseClass(classes[i], true); 082 // _out("-------"); 083 // _out(_doc.exportMoML()); 084 // _out("-------"); 085 } else { 086 // _out("skipping inner class: " + classes[i]); 087 } 088 } 089 } catch (Exception e) { 090 System.out.println("ERROR " + e.getClass() + ": " + e.getMessage()); 091 e.printStackTrace(); 092 } 093 return true; 094 } 095 096 /** 097 * Retrieve the documentation for a particular class after it has been 098 * created. 099 */ 100 public static KeplerDocumentationAttribute getDoc(String name) { 101 return _table.get(name); 102 } 103 104 /** Retrieve all the documentation. */ 105 public static Map<String,KeplerDocumentationAttribute> getAllDocs() { 106 return _table; 107 } 108 109 /** Returns true if the given class name is deprecated. 110 * NOTE: this method does not look at the tags of the class; 111 * the doclet must be run on the source file first. 112 */ 113 public static boolean isClassDeprecated(String className) { 114 return _deprecated.contains(className); 115 } 116 117 /** Set if documentation will be generated for deprecated classes. */ 118 public static void setGenerateForDeprecated(boolean generate) { 119 _generateForDeprecated = generate; 120 } 121 122 public static void setWorkspace(Workspace workspace) { 123 _workspace = workspace; 124 } 125 126 /** Create documentation for a class. */ 127 private static void _parseClass(ClassDoc classDoc, boolean printHeader) 128 throws IllegalActionException, NameDuplicationException, Exception { 129 // _out(""); 130 // _out("Class: " + classDoc); 131 // _out("commentText: " + classDoc.commentText()); 132 133 //String name = classDoc.name(); 134 135 if (printHeader) { 136 // _out("parsing for " + name); 137 138 if(_workspace == null) { 139 _workspace = new Workspace(); 140 } 141 142 _doc = new KeplerDocumentationAttribute(_workspace); 143 _doc.setName("KeplerDocumentation"); 144 145 _table.put(classDoc.qualifiedName(), _doc); 146 147 // add docs for the authors 148 String authors = _getAndCombineTags("@author", classDoc); 149 if (authors.length() == 0) { 150 _warn("No authors for " + classDoc.qualifiedName()); 151 } else { 152 _doc.setAuthor(authors); 153 } 154 155 // add docs for the class 156 String comment = classDoc.commentText(); 157 if (comment.length() == 0) { 158 _warn("No comments for class " + classDoc.qualifiedName()); 159 } else { 160 _doc.setUserLevelDocumentation("\n" + comment); 161 } 162 163 // add docs for the version 164 String version = _getAndCombineTags("@version", classDoc); 165 if(version.length() == 0) { 166 _warn("No version for " + classDoc.qualifiedName()); 167 } else { 168 // if the first character is $, remove everything after the second $ 169 version = version.trim(); 170 if(version.charAt(0) == '$') { 171 final int index = version.indexOf('$', 1); 172 if(index > 1) { 173 version = version.substring(0, index+1); 174 } 175 } 176 _doc.setVersion(version); 177 } 178 } 179 180 // add docs for all the fields 181 FieldDoc[] fields = classDoc.fields(); 182 for (int i = 0; i < fields.length; i++) { 183 FieldDoc curField = fields[i]; 184 Type type = curField.type(); 185 186 // add docs for a field if it's public, and a class 187 if (curField.isPublic() && !type.isPrimitive()) { 188 _parseField(curField); 189 } 190 } 191 192 // recursively add documentation of the parent class 193 ClassDoc superClass = classDoc.superclass(); 194 if (superClass != null) { 195 if (!superClass.toString().equals("ptolemy.actor.TypedAtomicActor") 196 && !superClass.toString().equals( 197 "ptolemy.actor.TypedCompositeActor")) { 198 // _out("recursing super class " + superClass.qualifiedName()); 199 _parseClass(superClass, false); 200 } 201 } 202 } 203 204 /** Return the FieldType of the field. */ 205 private static FieldType _getFieldType(String className) 206 throws ClassNotFoundException { 207 // _out("check field:" + className); 208 209 if (className.equals("ptolemy.actor.TypedIOPort")) { 210 return FieldType.Port; 211 } else if (className.equals("ptolemy.actor.parameters.PortParameter")) { 212 return FieldType.PortParameter; 213 } else if (className.equals("ptolemy.data.expr.Parameter") || 214 className.equals("ptolemy.kernel.util.StringAttribute")) { 215 return FieldType.Parameter; 216 } else if (className.equals("java.lang.Object")) { 217 return FieldType.Unknown; 218 } else { 219 String superClsStr = null; 220 Class<?> cls = null; 221 try { 222 cls = Class.forName(className); 223 } catch(ClassNotFoundException e) { 224 // try converting to a nested class 225 String nestedName = new StringBuilder(className).replace( 226 className.lastIndexOf('.'), 227 className.lastIndexOf('.') + 1, "$").toString(); 228 cls = Class.forName(nestedName); 229 } 230 if(cls != null) { 231 if(cls.isInterface()) { 232 return FieldType.Ignore; 233 } 234 Class<?> superCls = cls.getSuperclass(); 235 if(superCls != null) { 236 superClsStr = superCls.toString(); 237 } 238 } 239 240 if (superClsStr == null) { 241 _warn("could not determine super class for: " + className); 242 return FieldType.Unknown; 243 } else if(superClsStr.indexOf("class ") != 0) { 244 _warn("super class name does not begin with class: " + superClsStr); 245 return FieldType.Unknown; 246 } else { 247 return _getFieldType(superClsStr.substring(6)); 248 } 249 } 250 } 251 252 /** Create documentation for a port or parameter. */ 253 private static void _parseField(FieldDoc fieldDoc) 254 throws IllegalActionException, NameDuplicationException, Exception { 255 // _out(""); 256 // _out("field: " + fieldDoc); 257 // _out("type: " + fieldDoc.type()); 258 // _out("commentText: " + fieldDoc.commentText().replaceAll("\n", "")); 259 260 String fullName = fieldDoc.qualifiedName(); 261 String name = fieldDoc.name(); 262 263 FieldType type = _getFieldType(fieldDoc.type().asClassDoc() 264 .qualifiedName()); 265 266 if (type == FieldType.Unknown) { 267 //_out("skipping unknown type of field " + fullName + 268 //" type: " + fieldDoc.type()); 269 } else if (type != FieldType.Ignore) { 270 boolean unhandled = true; 271 String comment = fieldDoc.commentText().replaceAll("\n", ""); 272 if (comment.length() == 0) { 273 _warn("No comments for " + type.getName() + " " + fullName); 274 } 275 276 if (type == FieldType.Port || type == FieldType.PortParameter) { 277 _doc.addPort(name, comment); 278 unhandled = false; 279 } 280 281 if (type == FieldType.Parameter || type == FieldType.PortParameter) { 282 _doc.addProperty(name, comment); 283 unhandled = false; 284 } 285 286 if(unhandled) { 287 _warn("Unhandled field type: " + type); 288 } 289 } 290 } 291 292 private static String _getAndCombineTags(String tag, Doc doc) { 293 String retval = ""; 294 Tag[] tags = doc.tags(tag); 295 if (tags != null && tags.length > 0) { 296 StringBuffer buf = new StringBuffer(); 297 for (int i = 0; i < tags.length; i++) { 298 buf.append(tags[i].text() + ", "); 299 } 300 301 retval = buf.toString(); 302 // remove last comma 303 retval = retval.replaceAll("\\n", ""); 304 retval = retval.replaceAll("\\s*, $", ""); 305 } 306 return retval; 307 } 308 309 private static void _out(String str) { 310 System.out.println(str); 311 } 312 313 private static void _warn(String str) { 314 _out("WARNING: " + str); 315 } 316 317 /** A mapping of class name to documentation. */ 318 private static Map<String, KeplerDocumentationAttribute> _table = 319 new HashMap<String,KeplerDocumentationAttribute>(); 320 321 /** The current doc. */ 322 private static KeplerDocumentationAttribute _doc; 323 324 /** The types of fields. */ 325 private enum FieldType { 326 Parameter("parameter"), 327 Port("port"), 328 PortParameter("port parameter"), 329 Unknown("unknown"), 330 Ignore("ignore"); 331 332 FieldType(String name) { 333 _name = name; 334 } 335 336 public String getName() { 337 return _name; 338 } 339 340 private String _name; 341 }; 342 343 private static Workspace _workspace; 344 345 /** If false, do not generate documentation for deprecated classes. */ 346 private static boolean _generateForDeprecated = true; 347 348 /** A collection of deprecated classes. */ 349 private static Set<String> _deprecated = new HashSet<String>(); 350}