View Javadoc

1   package org.codehaus.mojo.taglist;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.FileReader;
24  import java.io.IOException;
25  import java.io.LineNumberReader;
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import java.util.HashMap;
29  import java.util.Iterator;
30  import java.util.List;
31  import java.util.Map;
32  
33  import org.apache.maven.plugin.logging.Log;
34  import org.apache.maven.reporting.MavenReportException;
35  import org.codehaus.mojo.taglist.beans.FileReport;
36  import org.codehaus.mojo.taglist.beans.TagReport;
37  import org.codehaus.plexus.util.FileUtils;
38  import org.codehaus.plexus.util.IOUtil;
39  
40  /**
41   * Class that analyses a file with a special comment tag. For instance:
42   * 
43   * <pre>
44   * // TODO: Example of an Eclipse/IntelliJ-like "todo" tag
45   * </pre>
46   * 
47   * @author <a href="mailto:bellingard.NO-SPAM@gmail.com">Fabrice Bellingard </a>
48   * @todo : This is another example of "todo" tag
49   */
50  public class FileAnalyser
51  {
52      /**
53       * String that is used for beginning a comment line.
54       */
55      private static final String STAR_COMMENT = "*";
56  
57      /**
58       * String that is used for beginning a comment line.
59       */
60      private static final String SLASH_COMMENT = "//";
61  
62      /**
63       * The directories to analyse.
64       */
65      private Collection sourceDirs;
66  
67      /**
68       * Log for debug output.
69       */
70      private Log log;
71  
72      /**
73       * Map containing tag names as keys ("TODO" or "@todo" for instance), and a TagReport object as value.
74       */
75      private Map tagReportsMap;
76  
77      /**
78       * Set to true if the analyser should look for multiple line comments.
79       */
80      private boolean multipleLineCommentsOn;
81  
82      /**
83       * Set to true if the analyser should look for tags without comments.
84       */
85      private boolean emptyCommentsOn;
86  
87      /**
88       * String used to indicate that there is no comment after the tag.
89       */
90      private String noCommentString;
91  
92      /**
93       * Constructor.
94       * 
95       * @param report the MOJO that is using this analyser.
96       */
97      public FileAnalyser( TagListReport report )
98      {
99          multipleLineCommentsOn = report.isMultipleLineComments();
100         emptyCommentsOn = report.isEmptyComments();
101         log = report.getLog();
102         sourceDirs = report.constructSourceDirs();
103         noCommentString = report.getBundle().getString( "report.taglist.nocomment" );
104         // init the map of tag reports
105         String[] tags = report.getTags();
106         tagReportsMap = new HashMap( tags.length );
107         for ( int i = 0; i < tags.length; i++ )
108         {
109             String tagName = tags[i];
110             TagReport tagReport = new TagReport( tagName );
111             tagReportsMap.put( tagName, tagReport );
112         }
113     }
114 
115     /**
116      * Execute the analysis for the configuration given by the TagListReport.
117      * 
118      * @return a collection of TagReport objects.
119      * @throws MavenReportException the Maven report exception.
120      */
121     public Collection execute()
122         throws MavenReportException
123     {
124         List fileList = findFilesToScan();
125 
126         for ( Iterator iter = fileList.iterator(); iter.hasNext(); )
127         {
128             File file = (File) iter.next();
129             if ( file.exists() )
130             {
131                 scanFile( file );
132             }
133         }
134 
135         return tagReportsMap.values();
136     }
137 
138     /**
139      * Gives the list of files to scan.
140      * 
141      * @return a List of File objects.
142      * @throws MavenReportException the Maven report exception.
143      */
144     private List findFilesToScan()
145         throws MavenReportException
146     {
147         List filesList = new ArrayList();
148         try
149         {
150             for ( Iterator iter = sourceDirs.iterator(); iter.hasNext(); )
151             {
152                 filesList.addAll( FileUtils.getFiles( new File( (String) iter.next() ), "**/*.java", null ) );
153             }
154         }
155         catch ( IOException e )
156         {
157             throw new MavenReportException( "Error while trying to find the files to scan.", e );
158         }
159         return filesList;
160     }
161 
162     /**
163      * Scans a file to look for task tags.
164      * 
165      * @param file the file to scan.
166      */
167     public void scanFile( File file )
168     {
169         LineNumberReader reader = null;
170 
171         try
172         {
173             reader = new LineNumberReader( new FileReader( file ) );
174 
175             String currentLine = reader.readLine();
176             while ( currentLine != null )
177             {
178                 int index = -1;
179                 String tagName = null;
180 
181                 int[] indices = new int[tagReportsMap.keySet().size()];
182                 String[] tagNames = new String[tagReportsMap.keySet().size()];
183                 int counter = 0;
184                 boolean found = false;
185                 // look for a tag on this line
186                 for ( Iterator iter = tagReportsMap.keySet().iterator(); iter.hasNext(); )
187                 {
188                     tagName = (String) iter.next();
189                     index = currentLine.indexOf( tagName );
190                     tagNames[counter] = tagName;
191                     indices[counter] = index;
192                     if ( index >= 0 )
193                     {
194                         found = true;
195                     }
196 
197                     counter++;
198                 }
199 
200                 if ( !found || tagName == null )
201                 {
202                     // no tag on this line: just go on next line
203                     currentLine = reader.readLine();
204                     continue;
205                 }
206 
207                 // there's a tag on this line
208                 String commentType = null;
209                 for ( int i = 0; i < indices.length; i++ )
210                 {
211                     if ( indices[i] >= 0 )
212                     {
213                         commentType = extractCommentType( currentLine, indices[i] );
214                     }
215                     if ( commentType != null )
216                     {
217                         index = indices[i];
218                         tagName = tagNames[i];
219                         break;
220                     }
221                 }
222 
223                 if ( commentType == null )
224                 {
225                     // this is not a valid comment tag: go to the next line
226                     currentLine = reader.readLine();
227                     continue;
228                 }
229 
230                 int tagLength = tagName.length();
231                 int commentStartIndex = reader.getLineNumber();
232                 StringBuffer comment = new StringBuffer();
233 
234                 String firstLine = currentLine.substring( index + tagLength ).trim();
235                 if ( firstLine.length() == 0 )
236                 {
237                     // this is not a valid comment tag: nothing is written there
238                     currentLine = reader.readLine();
239                     if ( emptyCommentsOn )
240                     {
241                         comment.append( "--" );
242                         comment.append( noCommentString );
243                         comment.append( "--" );
244                     }
245                     else
246                     {
247                         continue;
248                     }
249                 }
250                 else
251                 {
252                     // this tag has a comment
253                     if ( firstLine.charAt( 0 ) == ':' )
254                     {
255                         comment.append( firstLine.substring( 1 ).trim() );
256                     }
257                     else
258                     {
259                         comment.append( firstLine );
260                     }
261 
262                     // next line
263                     currentLine = reader.readLine();
264 
265                     if ( multipleLineCommentsOn )
266                     {
267                         // we're looking for multiple line comments
268                         while ( currentLine != null && currentLine.trim().startsWith( commentType )
269                             && currentLine.indexOf( tagName ) < 0 )
270                         {
271                             String currentComment = currentLine.substring( currentLine.indexOf( commentType )
272                                                                            + commentType.length() ).trim();
273                             if ( currentComment.startsWith( "@" ) || "".equals( currentComment )
274                                 || "/".equals( currentComment ) )
275                             {
276                                 // the comment is finished
277                                 break;
278                             }
279                             // try to look if the next line is not a new tag
280                             boolean newTagFound = false;
281                             for ( Iterator iter = tagReportsMap.keySet().iterator(); iter.hasNext(); )
282                             {
283                                 String currentTagName = (String) iter.next();
284                                 if ( currentComment.startsWith( currentTagName ) )
285                                 {
286                                     newTagFound = true;
287                                 }
288                             }
289                             if ( newTagFound )
290                             {
291                                 // this is a new comment: stop here the current comment
292                                 break;
293                             }
294                             // nothing was found: this means the comment is going on this line
295                             comment.append( " " );
296                             comment.append( currentComment );
297                             currentLine = reader.readLine();
298                         }
299                     }
300                 }
301 
302                 TagReport tagReport = (TagReport) tagReportsMap.get( tagName );
303                 FileReport fileReport = tagReport.getFileReport( file );
304                 fileReport.addComment( comment.toString(), commentStartIndex );
305             }
306         }
307         catch ( IOException e )
308         {
309             log.error( "Error while scanning the file " + file.getPath(), e );
310         }
311         finally
312         {
313             IOUtil.close( reader );
314         }
315     }
316 
317     /**
318      * Finds the type of comment the tag is in.
319      * 
320      * @param currentLine the line to analyse.
321      * @param index the index of the tag in the line.
322      * @return "*" or "//" or null.
323      */
324     private String extractCommentType( String currentLine, int index )
325     {
326         String commentType = null;
327         String beforeTag = currentLine.substring( 0, index ).trim();
328         if ( beforeTag.endsWith( SLASH_COMMENT ) )
329         {
330             commentType = SLASH_COMMENT;
331         }
332         else if ( beforeTag.endsWith( STAR_COMMENT ) )
333         {
334             commentType = STAR_COMMENT;
335         }
336         return commentType;
337     }
338 
339 }