View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.felix.bundleplugin.baseline;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.OutputStream;
25  import java.util.Locale;
26  import java.util.Map;
27  import java.util.ResourceBundle;
28  
29  import org.apache.maven.doxia.sink.Sink;
30  import org.apache.maven.plugins.annotations.LifecyclePhase;
31  import org.apache.maven.plugins.annotations.Mojo;
32  import org.apache.maven.plugins.annotations.Parameter;
33  import org.apache.maven.reporting.MavenReport;
34  import org.apache.maven.reporting.MavenReportException;
35  import org.codehaus.plexus.util.IOUtil;
36  
37  import aQute.bnd.version.Version;
38  
39  /**
40   * BND Baseline report.
41   *
42   * @since 2.4.1
43   */
44  @Mojo( name = "baseline-report", threadSafe = true,
45         defaultPhase = LifecyclePhase.SITE)
46  public final class BaselineReport
47      extends AbstractBaselinePlugin
48      implements MavenReport
49  {
50  
51      /**
52       * Specifies the directory where the report will be generated.
53       */
54      @Parameter(defaultValue = "${project.reporting.outputDirectory}")
55      private File outputDirectory;
56  
57      private static final class Context {
58          public Sink sink;
59  
60          public Locale locale;
61  
62          public int currentDepth = 0;
63      }
64  
65      // AbstractBaselinePlugin events
66  
67      @Override
68      protected Object init(final Object context)
69      {
70          failOnError = false;
71          failOnWarning = false;
72  
73          final File baselineImagesDirectory = new File( outputDirectory, "images/baseline" );
74          baselineImagesDirectory.mkdirs();
75  
76          for ( String resourceName : new String[]{ "access.gif",
77                                                    "annotated.gif",
78                                                    "annotation.gif",
79                                                    "bundle.gif",
80                                                    "class.gif",
81                                                    "constant.gif",
82                                                    "enum.gif",
83                                                    "error.gif",
84                                                    "extends.gif",
85                                                    "field.gif",
86                                                    "implements.gif",
87                                                    "info.gif",
88                                                    "interface.gif",
89                                                    "method.gif",
90                                                    "package.gif",
91                                                    "resource.gif",
92                                                    "return.gif",
93                                                    "version.gif",
94                                                    "warning.gif" } )
95          {
96              InputStream source = getClass().getResourceAsStream( resourceName );
97              OutputStream target = null;
98              File targetFile = new File( baselineImagesDirectory, resourceName );
99  
100             try
101             {
102                 target = buildContext.newFileOutputStream( targetFile );
103                 IOUtil.copy( source, target );
104             }
105             catch ( IOException e )
106             {
107                 getLog().warn( "Impossible to copy " + resourceName + " image, maybe the site won't be properly rendered." );
108             }
109             finally
110             {
111                 IOUtil.close( source );
112                 IOUtil.close( target );
113             }
114         }
115         return context;
116     }
117 
118     @Override
119     protected void close(final Object context)
120     {
121         // nothing to do
122     }
123 
124     @Override
125     protected void startBaseline( final Object context, String generationDate, String bundleName,
126             String currentVersion, String previousVersion )
127     {
128         final Context ctx = (Context)context;
129         final Sink sink = ctx.sink;
130 
131         sink.head();
132         sink.title();
133 
134         String title = getBundle( ctx.locale ).getString( "report.baseline.title" );
135         sink.text( title );
136         sink.title_();
137         sink.head_();
138 
139         sink.body();
140 
141         sink.section1();
142         sink.sectionTitle1();
143         sink.text( title );
144         sink.sectionTitle1_();
145 
146         sink.paragraph();
147         sink.text( getBundle( ctx.locale ).getString( "report.baseline.bndlink" ) + " " );
148         sink.link( "http://www.aqute.biz/Bnd/Bnd" );
149         sink.text( "Bnd" );
150         sink.link_();
151         sink.text( "." );
152         sink.paragraph_();
153 
154         sink.paragraph();
155         sink.text( getBundle( ctx.locale ).getString( "report.baseline.bundle" ) + " " );
156         sink.figure();
157         sink.figureGraphics( "images/baseline/bundle.gif" );
158         sink.figure_();
159         sink.text( " " );
160         sink.bold();
161         sink.text( bundleName );
162         sink.bold_();
163         sink.listItem_();
164 
165         sink.paragraph();
166         sink.text( getBundle( ctx.locale ).getString( "report.baseline.version.current" ) + " " );
167         sink.bold();
168         sink.text( currentVersion );
169         sink.bold_();
170         sink.paragraph_();
171 
172         sink.paragraph();
173         sink.text( getBundle( ctx.locale ).getString( "report.baseline.version.comparison" ) + " " );
174         sink.bold();
175         sink.text( comparisonVersion );
176         sink.bold_();
177         sink.paragraph_();
178 
179         sink.paragraph();
180         sink.text( getBundle( ctx.locale ).getString( "report.baseline.generationdate" ) + " " );
181         sink.bold();
182         sink.text( generationDate );
183         sink.bold_();
184         sink.paragraph_();
185 
186         sink.section1_();
187     }
188 
189     @Override
190     protected void startPackage( final Object context,
191                                  boolean mismatch,
192                                  String packageName,
193                                  String shortDelta,
194                                  String delta,
195                                  Version newerVersion,
196                                  Version olderVersion,
197                                  Version suggestedVersion,
198                                  DiffMessage diffMessage,
199                                  Map<String,String> attributes )
200     {
201         final Context ctx = (Context)context;
202         final Sink sink = ctx.sink;
203 
204         sink.list();
205 
206         sink.listItem();
207 
208         sink.figure();
209         sink.figureGraphics( "./images/baseline/package.gif" );
210         sink.figure_();
211         sink.text( " " );
212         sink.monospaced();
213         sink.text( packageName );
214         sink.monospaced_();
215 
216         if ( diffMessage != null )
217         {
218             sink.text( " " );
219             sink.figure();
220             sink.figureGraphics( "./images/baseline/" + diffMessage.getType().name() + ".gif" );
221             sink.figure_();
222             sink.text( " " );
223             sink.italic();
224             sink.text( diffMessage.getMessage() );
225             sink.italic_();
226             sink.text( " (newer version: " );
227             sink.monospaced();
228             sink.text( newerVersion.toString() );
229             sink.monospaced_();
230             sink.text( ", older version: " );
231             sink.monospaced();
232             sink.text( olderVersion.toString() );
233             if ( suggestedVersion != null )
234             {
235                 sink.monospaced_();
236                 sink.text( ", suggested version: " );
237                 sink.monospaced();
238                 sink.text( suggestedVersion.toString() );
239             }
240             sink.monospaced_();
241             sink.text( ")" );
242         }
243     }
244 
245     @Override
246     protected void startDiff( final Object context,
247                               int depth,
248                               String type,
249                               String name,
250                               String delta,
251                               String shortDelta )
252     {
253         final Context ctx = (Context)context;
254         final Sink sink = ctx.sink;
255 
256         if ( ctx.currentDepth < depth )
257         {
258             sink.list();
259         }
260 
261         ctx.currentDepth = depth;
262 
263         sink.listItem();
264         sink.figure();
265         sink.figureGraphics( "images/baseline/" + type + ".gif" );
266         sink.figure_();
267         sink.text( " " );
268 
269         sink.monospaced();
270         sink.text( name );
271         sink.monospaced_();
272 
273         sink.text( " " );
274 
275         sink.italic();
276         sink.text( delta );
277         sink.italic_();
278     }
279 
280     @Override
281     protected void endDiff( final Object context, int depth )
282     {
283         final Context ctx = (Context)context;
284         final Sink sink = ctx.sink;
285 
286         sink.listItem_();
287 
288         if ( ctx.currentDepth > depth )
289         {
290             sink.list_();
291         }
292 
293         ctx.currentDepth = depth;
294     }
295 
296     @Override
297     protected void endPackage(final Object context)
298     {
299         final Context ctx = (Context)context;
300         final Sink sink = ctx.sink;
301         if ( ctx.currentDepth > 0 )
302         {
303             sink.list_();
304             ctx.currentDepth = 0;
305         }
306 
307         sink.listItem_();
308         sink.list_();
309     }
310 
311     @Override
312     protected void endBaseline(final Object context)
313     {
314         final Context ctx = (Context)context;
315         ctx.sink.body_();
316         ctx.sink.flush();
317         ctx.sink.close();
318     }
319 
320     // MavenReport methods
321 
322     public boolean canGenerateReport()
323     {
324         return !skip && outputDirectory != null;
325     }
326 
327     public void generate( @SuppressWarnings( "deprecation" ) org.codehaus.doxia.sink.Sink sink, Locale locale )
328         throws MavenReportException
329     {
330         final Context ctx = new Context();
331         ctx.sink = sink;
332         ctx.locale = locale;
333 
334         try
335         {
336             execute(ctx);
337         }
338         catch ( Exception e )
339         {
340             getLog().warn( "An error occurred while producing the report page, see nested exceptions", e );
341         }
342     }
343 
344     public String getCategoryName()
345     {
346         return MavenReport.CATEGORY_PROJECT_REPORTS;
347     }
348 
349     public String getDescription( Locale locale )
350     {
351         return getBundle( locale ).getString( "report.baseline.description" );
352     }
353 
354     public String getName( Locale locale )
355     {
356         return getBundle( locale ).getString( "report.baseline.name" );
357     }
358 
359     private ResourceBundle getBundle( Locale locale )
360     {
361         return ResourceBundle.getBundle( "baseline-report", locale, getClass().getClassLoader() );
362     }
363 
364     public String getOutputName()
365     {
366         return "baseline-report";
367     }
368 
369     public File getReportOutputDirectory()
370     {
371         return outputDirectory;
372     }
373 
374     public boolean isExternalReport()
375     {
376         return false;
377     }
378 
379     public void setReportOutputDirectory( File outputDirectory )
380     {
381         this.outputDirectory = outputDirectory;
382     }
383 
384 }