View Javadoc

1   package org.apache.maven.plugin.jira;
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 org.apache.maven.doxia.sink.Sink;
23  import org.apache.maven.plugin.MojoExecutionException;
24  import org.apache.maven.plugin.logging.Log;
25  import org.apache.maven.reporting.MavenReportException;
26  
27  import java.io.File;
28  import java.util.ArrayList;
29  import java.util.List;
30  import java.util.ResourceBundle;
31  
32  /**
33   * Generates a JIRA report.
34   *
35   * @version $Id: JiraReportGenerator.html 816592 2012-05-08 12:40:21Z hboutemy $
36   */
37  public class JiraReportGenerator
38  {
39      private static final int COLUMN_KEY = 0;
40      private static final int COLUMN_SUMMARY = 1;
41      private static final int COLUMN_STATUS = 2;
42      private static final int COLUMN_RESOLUTION = 3;
43      private static final int COLUMN_ASSIGNEE = 4;
44      private static final int COLUMN_REPORTER = 5;
45      private static final int COLUMN_TYPE = 6;
46      private static final int COLUMN_PRIORITY = 7;
47      private static final int COLUMN_VERSION = 8;
48      private static final int COLUMN_FIX_VERSION = 9;
49      private static final int COLUMN_COMPONENT = 10;
50  
51      private static final String[] JIRA_COLUMNS = new String[] {
52          /* 0  */ "Key",
53          /* 1  */ "Summary",
54          /* 2  */ "Status",
55          /* 3  */ "Resolution",
56          /* 4  */ "Assignee",
57          /* 5  */ "Reporter",
58          /* 6  */ "Type",
59          /* 7  */ "Priority",
60          /* 8  */ "Version",
61          /* 9  */ "Fix Version",
62          /* 10 */ "Component"
63      };
64  
65      private static final String SNAPSHOT_SUFFIX = "-SNAPSHOT";
66  
67      private int[] columnOrder;
68  
69      private String currentVersion = null;
70  
71      private JiraXML jira;
72  
73      private boolean onlyCurrentVersion = false;
74  
75      public JiraReportGenerator()
76      {
77  
78      }
79  
80      /**
81       *
82       * @param xmlFile An xml file containing issues from JIRA
83       * @param columnNames The names of the columns to include in the report
84       * @param currentVersion The current version of the project
85       * @param onlyCurrentVersion If only issues for the current version will be included in the report
86       */
87      public JiraReportGenerator( File xmlFile, String columnNames, String currentVersion, boolean onlyCurrentVersion )
88          throws MavenReportException
89      {
90          this.currentVersion = currentVersion;
91          this.onlyCurrentVersion = onlyCurrentVersion;
92  
93          jira = new JiraXML( xmlFile );
94  
95          String[] columnNamesArray = columnNames.split( "," );
96          int validColumnNames = 0;
97          columnOrder = new int[columnNamesArray.length];
98          for ( int i = 0; i < columnOrder.length; i++ )
99          {
100             // Default to -1, indicating that the column should not be included in the report
101             columnOrder[i] = -1;
102             for ( int columnIndex = 0; columnIndex < JIRA_COLUMNS.length; columnIndex++ )
103             {
104                 String columnName = columnNamesArray[i].trim();
105                 if ( JIRA_COLUMNS[columnIndex].equalsIgnoreCase( columnName ) )
106                 {
107                     // Found a valid column name - add it
108                     columnOrder[i] = columnIndex;
109                     validColumnNames++;
110                     break;
111                 }
112             }
113         }
114         if ( validColumnNames == 0 )
115         {
116             // This can happen if the user has configured column names and they are all invalid
117             throw new MavenReportException(
118                 "maven-changes-plugin: None of the configured columnNames '" + columnNames + "' are valid." );
119         }
120     }
121 
122     public void doGenerateEmptyReport( ResourceBundle bundle, Sink sink )
123     {
124         sinkBeginReport( sink, bundle );
125 
126         sink.paragraph();
127 
128         sink.text( bundle.getString( "report.jira.error" ) );
129 
130         sink.paragraph_();
131 
132         sinkEndReport( sink );
133     }
134 
135     public void doGenerateReport( ResourceBundle bundle, Sink sink, Log log )
136         throws MojoExecutionException
137     {
138         List issueList = jira.getIssueList();
139 
140         if ( onlyCurrentVersion )
141         {
142             issueList = getIssuesForCurrentRelease( issueList );
143             log.info( "The JIRA Report will contain issues only for the current version." );
144         }
145 
146         sinkBeginReport( sink, bundle );
147 
148         constructHeaderRow( sink, issueList, bundle );
149 
150         constructDetailRows( sink, issueList );
151 
152         sinkEndReport( sink );
153     }
154 
155     private void constructHeaderRow( Sink sink, List issueList, ResourceBundle bundle )
156     {
157         if ( issueList == null )
158         {
159             return;
160         }
161 
162         sink.table();
163 
164         sink.tableRow();
165 
166         for ( int columnIndex = 0; columnIndex < columnOrder.length; columnIndex++ )
167         {
168             switch ( columnOrder[columnIndex] )
169             {
170                 case COLUMN_KEY:
171                     sinkHeader( sink, bundle.getString( "report.jira.label.key" ) );
172                     break;
173 
174                 case COLUMN_SUMMARY:
175                     sinkHeader( sink, bundle.getString( "report.jira.label.summary" ) );
176                     break;
177 
178                 case COLUMN_STATUS:
179                     sinkHeader( sink, bundle.getString( "report.jira.label.status" ) );
180                     break;
181 
182                 case COLUMN_RESOLUTION:
183                     sinkHeader( sink, bundle.getString( "report.jira.label.resolution" ) );
184                     break;
185 
186                 case COLUMN_ASSIGNEE:
187                     sinkHeader( sink, bundle.getString( "report.jira.label.by" ) );
188                     break;
189 
190                 case COLUMN_REPORTER:
191                     sinkHeader( sink, bundle.getString( "report.jira.label.reporter" ) );
192                     break;
193 
194                 case COLUMN_TYPE:
195                     sinkHeader( sink, bundle.getString( "report.jira.label.type" ) );
196                     break;
197 
198                 case COLUMN_PRIORITY:
199                     sinkHeader( sink, bundle.getString( "report.jira.label.priority" ) );
200                     break;
201 
202                 case COLUMN_VERSION:
203                     sinkHeader( sink, bundle.getString( "report.jira.label.version" ) );
204                     break;
205 
206                 case COLUMN_FIX_VERSION:
207                     sinkHeader( sink, bundle.getString( "report.jira.label.fixVersion" ) );
208                     break;
209 
210                 case COLUMN_COMPONENT:
211                     sinkHeader( sink, bundle.getString( "report.jira.label.component" ) );
212                     break;
213 
214                 default:
215                     // Do not add a header for this column
216                     break;
217             }
218 
219         }
220 
221         sink.tableRow_();
222     }
223 
224     private void constructDetailRows( Sink sink, List issueList )
225     {
226         if ( issueList == null )
227         {
228             return;
229         }
230 
231         for ( int idx = 0; idx < issueList.size(); idx++ )
232         {
233             JiraIssue issue = (JiraIssue) issueList.get( idx );
234 
235             sink.tableRow();
236 
237             for ( int columnIndex = 0; columnIndex < columnOrder.length; columnIndex++ )
238             {
239                 switch ( columnOrder[columnIndex] )
240                 {
241                     case COLUMN_KEY:
242                         sink.tableCell();
243                         sink.link( issue.getLink() );
244                         sink.text( issue.getKey() );
245                         sink.link_();
246                         sink.tableCell_();
247                         break;
248 
249                     case COLUMN_SUMMARY:
250                         sinkCell( sink, issue.getSummary() );
251                         break;
252 
253                     case COLUMN_STATUS:
254                         sinkCell( sink, issue.getStatus() );
255                         break;
256 
257                     case COLUMN_RESOLUTION:
258                         sinkCell( sink, issue.getResolution() );
259                         break;
260 
261                     case COLUMN_ASSIGNEE:
262                         sinkCell( sink, issue.getAssignee() );
263                         break;
264 
265                     case COLUMN_REPORTER:
266                         sinkCell( sink, issue.getReporter() );
267                         break;
268 
269                     case COLUMN_TYPE:
270                         sinkCell( sink, issue.getType() );
271                         break;
272 
273                     case COLUMN_PRIORITY:
274                         sinkCell( sink, issue.getPriority() );
275                         break;
276 
277                     case COLUMN_VERSION:
278                         sinkCell( sink, issue.getVersion() );
279                         break;
280 
281                     case COLUMN_FIX_VERSION:
282                         sinkCell( sink, issue.getFixVersion() );
283                         break;
284 
285                     case COLUMN_COMPONENT:
286                         sinkCell( sink, issue.getComponent() );
287                         break;
288 
289                     default:
290                         // Do not add a cell for this column
291                         break;
292                 }
293             }
294 
295             sink.tableRow_();
296         }
297 
298         sink.table_();
299     }
300 
301     private void sinkBeginReport( Sink sink, ResourceBundle bundle )
302     {
303         sink.head();
304 
305         sink.title();
306         sink.text( bundle.getString( "report.jira.header" ) );
307         sink.title_();
308 
309         sink.head_();
310 
311         sink.body();
312 
313         sink.section1();
314 
315         sinkSectionTitle1( sink, bundle.getString( "report.jira.header" ) );
316     }
317 
318     private void sinkEndReport( Sink sink )
319     {
320         sink.section1_();
321 
322         sink.body_();
323 
324         sink.flush();
325 
326         sink.close();
327     }
328 
329     private void sinkFigure( Sink sink, String image )
330     {
331         sink.figure();
332 
333         sink.figureGraphics( image );
334 
335         sink.figure_();
336     }
337 
338     private void sinkHeader( Sink sink, String header )
339     {
340         sink.tableHeaderCell();
341 
342         sink.text( header );
343 
344         sink.tableHeaderCell_();
345     }
346 
347     private void sinkCell( Sink sink, String text )
348     {
349         sink.tableCell();
350 
351         if ( text != null )
352         {
353             sink.text( text );
354         }
355         else
356         {
357             sink.rawText( "&nbsp;" );
358         }
359 
360         sink.tableCell_();
361     }
362 
363     private void sinkSectionTitle1( Sink sink, String text )
364     {
365         sink.sectionTitle1();
366 
367         sink.text( text );
368 
369         sink.sectionTitle1_();
370     }
371 
372     /**
373      * Find the issues for only the current release, by matching the "Fix for"
374      * version in the supplied list of issues with the current version from the
375      * pom. If the current version is a SNAPSHOT, then that part of the version
376      * will be removed prior to the matching.
377      *
378      * @param allIssues A list of all issues from JIRA
379      * @return A <code>List</code> of issues for the current release of the current project
380      * @throws org.apache.maven.plugin.MojoExecutionException
381      *          If no issues could be found for the current release
382      */
383     public List getIssuesForCurrentRelease( List allIssues )
384         throws MojoExecutionException
385     {
386         List currentReleaseIssues = new ArrayList();
387         boolean isFound = false;
388         JiraIssue issue = null;
389         String releaseVersion = currentVersion;
390 
391         // Remove "-SNAPSHOT" from the end of the version, if it's there
392         if ( currentVersion != null && currentVersion.endsWith( SNAPSHOT_SUFFIX ) )
393         {
394             releaseVersion = currentVersion.substring( 0, currentVersion.length() - SNAPSHOT_SUFFIX.length() );
395         }
396 
397         for ( int i = 0; i < allIssues.size(); i++ )
398         {
399             issue = (JiraIssue) allIssues.get( i );
400 
401             if ( issue.getFixVersion() != null && issue.getFixVersion().equals( releaseVersion ) )
402             {
403                 isFound = true;
404                 currentReleaseIssues.add( issue );
405             }
406         }
407 
408         if ( !isFound )
409         {
410             throw new MojoExecutionException(
411                 "Couldn't find any issues for the version '" + releaseVersion + "' among the supplied issues." );
412         }
413         return currentReleaseIssues;
414     }
415 }