001    package hirondelle.web4jtools.taglet;
002    
003    import java.util.*;
004    import com.sun.tools.doclets.Taglet;    
005    import com.sun.javadoc.Tag;               
006    import static hirondelle.web4j.util.Consts.NEW_LINE;
007    import hirondelle.web4j.util.Util;
008    
009    /**
010    * Taglet for linking to <tt>.sql</tt> files.
011    *
012    * <P>The <tt>.sql</tt> files need to be copied into the javadoc tree. 
013    * The typical use case is to place the <tt>.sql</tt> file in the same 
014    * directory as the class that references it. 
015    * 
016    * <P>Example 1 :<br>
017    * <tt>&ampsql statements.sql</tt>
018    *  
019    * <P>Example 2 :<br>
020    * <tt>&ampsql ../blah/statements.sql</tt>
021    * 
022    *  <P>This references a file in a nearby directory.
023    *  
024    * <P>Example 3 :<br>
025    * <tt>&ampsql statements.sql ADD_BLAH, CHANGE_BLAH</tt>
026    * <P>This example references two specific SQL statements in the <tt>statements.sql</tt> file. 
027    * That is, it references only a subset of the statements in the file.
028    */
029    public final class SqlTaglet implements Taglet {
030      
031      /** Register this taglet with the javadoc tool, under the taglet name 'sql'.  */
032      public static void register(Map aTagletMap){
033        if ( aTagletMap.get(TAGLET_NAME) != null ) {
034          aTagletMap.remove(TAGLET_NAME);
035        }
036        aTagletMap.put(TAGLET_NAME, new SqlTaglet());
037      }
038      
039      /** Return 'sql'. */
040      public String getName() {
041        return TAGLET_NAME;
042      }
043      
044      /** Returns <tt>false</tt>.  */
045      public boolean isInlineTag() {
046        return false;
047      }
048      
049      /** Returns <tt>false</tt>.  */
050      public boolean inOverview() {
051        return false;
052      }
053      
054      /** Returns <tt>false</tt>.  */
055      public boolean inPackage() {
056        return false;
057      }
058      
059      /** Returns <tt>true</tt>.  */
060      public boolean inType() {
061        return true;
062      }
063      
064      /** Returns <tt>false</tt>.  */
065      public boolean inConstructor() {
066        return false;
067      }
068      
069      /** Returns <tt>false</tt>.  */
070      public boolean inMethod() {
071        return false;
072      }
073      
074      /** Returns <tt>false</tt>.  */
075      public boolean inField() {
076        return false;
077      }
078      
079      /** Render a series of <tt>&amp;sql</tt> items.  */
080      public String toString(Tag[] aTags) {
081        StringBuilder result = new StringBuilder();
082        if ( aTags.length != 0) {
083          result.append(TAGLET_HEADER);
084          result.append(NEW_LINE);
085          result.append("<ul>" + NEW_LINE);
086          for(Tag tag: aTags){
087            if( Util.textHasContent(tag.text()) ) {
088              result.append("<li>" + getLink(tag) + NEW_LINE);
089            }
090          }
091          result.append("</ul>" + NEW_LINE);
092        }
093        return result.toString();
094      }
095    
096      /** Render a single <tt>&amp;sql</tt> item.  */
097      public String toString(Tag aTag) {
098        String result = "";
099        if( Util.textHasContent(aTag.text()) )  {
100          result = TAGLET_HEADER + getLink(aTag) + NEW_LINE;
101        }
102        return result;
103      }
104      
105      // PRIVATE //
106      private static final String TAGLET_NAME = "sql";
107      private static final String TAGLET_HEADER = "<b>SQL Statements :</b> ";
108      
109      private String getLink(Tag aTag){
110        ParsedTag parsedTag = new ParsedTag(aTag);
111        String result = "<a href='" + parsedTag.getFileName() + "'><tt>" + parsedTag.getFileName() + "</tt></a>";
112        if ( ! parsedTag.getOperations().isEmpty() ){
113          result = result + getOperation(parsedTag);
114          for (String operation : parsedTag.getOperations() ){
115            result = result + " <tt>" + operation + "</tt>";
116          }
117        }
118        return result;
119      }
120      
121      /** Return the list of operations, if any. */
122      private String getOperation(ParsedTag aParsedTag) {
123        String result = "";
124        if ( aParsedTag.getOperations().size() == 1 ){
125          result = result + " - Single Operation";
126        }
127        else {
128          result = result + " -  Operations (" + aParsedTag.getOperations().size() + ")";
129        }
130        result = result + ": ";
131        return result;
132      }
133      
134      /**
135      * Parse a tag's text content into two parts: a link/file name, and an optional list of operations.
136      * The operations are simply the names attached to SQL statements, in .sql files.
137      */
138      private static final class ParsedTag {
139        ParsedTag(Tag aTag){
140          parseTag(aTag.text());
141        }
142        String getFileName() { return fFileName; }
143        List<String> getOperations() {  return fOperations;   }
144        private void parseTag(String aRawTag){
145          StringTokenizer parser = new StringTokenizer(aRawTag);
146          fFileName = parser.nextToken();
147          while ( parser.hasMoreTokens() ) {
148            fOperations.add(parser.nextToken());
149          }
150        }
151        private String fFileName;
152        private List<String> fOperations = new ArrayList<String>();
153      }
154    }