package edu.uiuc.cs.fsl.propertydocs.taglets; import com.sun.javadoc.Tag; import com.sun.tools.doclets.Taglet; import edu.uiuc.cs.fsl.propertydocs.util.*; import java.io.File; import java.io.FileOutputStream; import java.io.ObjectOutputStream; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * This Taglet allows for opening a property property section in * a javadoc comment * * @author Patrick Meredith * */ public class PropertyOpenTaglet implements Taglet { static { CreatePropertyFile.forceInit(); } private static final String NAME = "property.open"; private static final String dir = System.getProperty( "outputpath" ); private File stats = new File(dir + File.separator + "__properties" + File.separator + "property.stats"); private static final String GLOBAL = "<global>"; private static final String ALLATTRIBUTES = " <all> "; private Set<PositionWrapper> seenDocs = new HashSet<PositionWrapper>(); private PropertyMap statsDB = new PropertyMap(0); public String getName() { return NAME; } /** can be used in a field comment*/ public boolean inField() { return true; } /**can be used in a constructor comment*/ public boolean inConstructor() { return true; } /**can be used in a method comment*/ public boolean inMethod() { return true; } /**can be used in overview comment*/ public boolean inOverview() { return true; } /**can be used in package comment*/ public boolean inPackage() { return true; } /**can be used in type comment (classes or interfaces)*/ public boolean inType() { return true; } /**this IS an inline tag*/ public boolean isInlineTag() { return true; } /** * Register this Taglet. * @param tagletMap the map to register this tag to. */ @SuppressWarnings("unchecked") public static void register(Map tagletMap) { PropertyOpenTaglet tag = new PropertyOpenTaglet(); Taglet t = (Taglet) tagletMap.get(tag.getName()); if (t != null) { tagletMap.remove(tag.getName()); } tagletMap.put(tag.getName(), tag); } /** * Given the <code>Tag</code> representation of this custom * tag, return its string representation. * * Here we have to do some annoying garbage because you there is no * way short of file io to share info between Taglets. Instead * I am going to parse all the inline tags if the whole document * and collect statistics on <b>all</b> the property sections in the * document. * * Statistics will only be collected if we have not already seen an * property.open tag for this document. We keep track of the document * by hashing its SourcePosition. The PositionWrapper makes these * hashable. * * @param tag he <code>Tag</code> representation of this custom tag. */ public String toString(Tag tag) { //System.out.println(tag.position()); PositionWrapper p = new PositionWrapper(tag.holder().position()); if(!(seenDocs.contains(p))){ seenDocs.add(p); handleTags(tag); } String[] arguments = tag.text().trim().split("\\s+"); int num = numLinks(arguments); String links = handleLinks(arguments, p, tag); if(num == 0){ return "<DIV CLASS=\"Red\" NAME=\"brokenproperty\"" + " ONMOUSEOVER=\"balloon.showTooltip(event, 'This is a property with no " + "formalizations.')\"" + " STYLE=\"display:inline\">"; } else if(num == 1){ return "<DIV CLASS=\"NavBarCell1Rev\" NAME=\"property\" ONMOUSEOVER=" + "\"balloon.showTooltip(event,'This describes a formalized property. " + "The property may be viewed by clicking on the link below:<br/>" + links + " ',1)\"" + " STYLE=\"display:inline\">"; } return "<DIV CLASS=\"NavBarCell1Rev\" NAME=\"property\" ONMOUSEOVER=" + "\"balloon.showTooltip(event,'This describes several formalized properties. " + "The properties may be viewed by clicking on the links below:<br/>" + links + " ',1)\"" + " STYLE=\"display:inline\">"; } private boolean isLink(String arg){ return arg.startsWith("Property:") || arg.startsWith("property:") || arg.startsWith("Formal:") || arg.startsWith("formal:"); } private int numLinks(String[] args){ int i = 0; for(String arg : args){ if(isLink(arg)) ++i; if(i > 1) return i; } return i; } private String handleLinks(String[] args, PositionWrapper p, Tag tag){ String ret = ""; for(String arg : args){ if(isLink(arg)) { String[] parts = arg.split(":"); if(parts.length != 2) throw new RuntimeException("too many ':''s in Property specification in comment at " + p); int depth = parts[1].split("[.]").length + 1; CreatePropertyFile.createOrModifyPropertyFile(parts[1], p, tag, depth); ret += "<A HREF=\\'" + GenerateUrls.buildRelativeUrl(tag) + "__properties/html/" + parts[1].replaceAll("[.]","/") + ".html\\'>" + parts[1] + "</A> <BR />"; } } //System.out.println("***********" + ret); return ret; } private void handleTags(Tag t){ Tag[] tags = t.holder().inlineTags(); boolean inProperty = false; //not sure if trim() is necessary. I doubt it is, but speed isn't //an issue here String packageName = GenerateUrls.getPackageDoc(t).toString().trim(); DefaultMap<String, Integer> globalStats = statsDB.get(GLOBAL); DefaultMap<String, Integer> packageStats = statsDB.get(packageName); //int c = 0; //character count int localW = 0; //word count for a given property tag pair //int l = 0; //line count String[] arguments = null; for(Tag tag: tags){ if(tag.name().equals("@property.open")){ arguments = tag.text().trim().split("\\s+"); localW = 0; inProperty = true; } else if(tag.name().equals("@property.close")){ Integer allGlobalW = globalStats.get(ALLATTRIBUTES); Integer allPackageW = packageStats.get(ALLATTRIBUTES); globalStats.put(ALLATTRIBUTES, allGlobalW + localW); packageStats.put(ALLATTRIBUTES, allPackageW + localW); for(String arg : arguments){ if(isLink(arg)) continue; if(arg.equals("")) continue; Integer argGlobalW = globalStats.get(arg); Integer argPacakgeW = packageStats.get(arg); globalStats.put(arg, argGlobalW + localW); packageStats.put(arg, argPacakgeW + localW); } inProperty = false; } else if(tag.name().equals("Text") && inProperty){ String text = tag.text().trim(); if(text.length() == 0) continue; localW += text.split("\\s+").length; } else if( (tag.name().equals("@description.close") || tag.name().equals("@description.open") || tag.name().equals("@inheritDoc")) && inProperty){ System.err.println("ERROR: " + tag.name() + " inside property environment in comment near " + tag.holder().position() + " is not allowed"); System.exit(1); } } if(inProperty) { System.err.println("ERROR: @property.open tag in comment near " + tags[0].holder().position() + " was not closed"); System.exit(1); } try { FileOutputStream fos = new FileOutputStream(stats); ObjectOutputStream ps = new ObjectOutputStream(fos); ps.writeObject(statsDB); ps.close(); } catch (java.io.IOException e){ throw new RuntimeException(e); } } /** * This method should not be called since arrays of inline tags do not * exist. Method {@link #toString(Tag)} should be used to convert this * inline tag to a string. * @param tags the array of <code>Tag</code>s representing of this custom tag. */ public String toString(Tag[] tags) { return null; } }