001package org.apache.maven.plugin.plugin;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import org.apache.maven.artifact.Artifact;
023import org.apache.maven.artifact.repository.ArtifactRepository;
024import org.apache.maven.doxia.sink.Sink;
025import org.apache.maven.doxia.siterenderer.Renderer;
026import org.apache.maven.model.Plugin;
027import org.apache.maven.plugin.descriptor.InvalidPluginDescriptorException;
028import org.apache.maven.plugin.descriptor.MojoDescriptor;
029import org.apache.maven.plugin.descriptor.PluginDescriptor;
030import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder;
031import org.apache.maven.plugins.annotations.Component;
032import org.apache.maven.plugins.annotations.Execute;
033import org.apache.maven.plugins.annotations.LifecyclePhase;
034import org.apache.maven.plugins.annotations.Mojo;
035import org.apache.maven.plugins.annotations.Parameter;
036import org.apache.maven.project.MavenProject;
037import org.apache.maven.reporting.AbstractMavenReport;
038import org.apache.maven.reporting.AbstractMavenReportRenderer;
039import org.apache.maven.reporting.MavenReportException;
040import org.apache.maven.tools.plugin.DefaultPluginToolsRequest;
041import org.apache.maven.tools.plugin.PluginToolsRequest;
042import org.apache.maven.tools.plugin.extractor.ExtractionException;
043import org.apache.maven.tools.plugin.generator.GeneratorException;
044import org.apache.maven.tools.plugin.generator.GeneratorUtils;
045import org.apache.maven.tools.plugin.generator.PluginXdocGenerator;
046import org.apache.maven.tools.plugin.scanner.MojoScanner;
047import org.apache.maven.tools.plugin.util.PluginUtils;
048import org.codehaus.plexus.component.repository.ComponentDependency;
049import org.codehaus.plexus.configuration.PlexusConfigurationException;
050import org.codehaus.plexus.util.StringUtils;
051import org.codehaus.plexus.util.xml.Xpp3Dom;
052
053import java.io.File;
054import java.io.FileNotFoundException;
055import java.io.FileReader;
056import java.util.ArrayList;
057import java.util.Iterator;
058import java.util.List;
059import java.util.Locale;
060import java.util.Map;
061import java.util.ResourceBundle;
062import java.util.Set;
063
064/**
065 * Generates the Plugin's documentation report: <code>plugin-info.html</code> plugin overview page,
066 * and one <code><i>goal</i>-mojo.html</code> per goal.
067 *
068 * @author <a href="snicoll@apache.org">Stephane Nicoll</a>
069 * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
070 * @version $Id: PluginReport.html 995996 2016-08-26 22:31:42Z rfscholte $
071 * @since 2.0
072 */
073@Mojo( name = "report", threadSafe = true )
074@Execute( phase = LifecyclePhase.PROCESS_CLASSES )
075public class PluginReport
076    extends AbstractMavenReport
077{
078    /**
079     * Report output directory for mojos' documentation.
080     */
081    @Parameter( defaultValue = "${project.build.directory}/generated-site/xdoc" )
082    private File outputDirectory;
083
084    /**
085     * Doxia Site Renderer.
086     */
087    @Component
088    private Renderer siteRenderer;
089
090    /**
091     * The Maven Project.
092     */
093    @Parameter( defaultValue = "${project}", readonly = true )
094    private MavenProject project;
095
096    /**
097     * Mojo scanner tools.
098     */
099    @Component
100    protected MojoScanner mojoScanner;
101
102    /**
103     * The file encoding of the source files.
104     *
105     * @since 2.7
106     */
107    @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" )
108    private String encoding;
109
110    /**
111     * Specify some requirements to execute this plugin.
112     * Example:
113     * <pre>
114     * &lt;requirements&gt;
115     *   &lt;maven&gt;2.0&lt;/maven&gt;
116     *   &lt;jdk&gt;1.4&lt;/jdk&gt;
117     *   &lt;memory&gt;256m&lt;/memory&gt;
118     *   &lt;diskSpace&gt;1m&lt;/diskSpace&gt;
119     *   &lt;others&gt;
120     *     &lt;property&gt;
121     *       &lt;name&gt;SVN&lt;/name&gt;
122     *       &lt;value&gt;1.4.6&lt;/value&gt;
123     *     &lt;/property&gt;
124     *   &lt;/others&gt;
125     * &lt;/requirements&gt;
126     * </pre>
127     */
128    @Parameter
129    private Requirements requirements;
130
131    /**
132     * The goal prefix that will appear before the ":".
133     * By default, this plugin applies a heuristic to derive a heuristic from
134     * the plugin's artifactId.
135     * <p/>
136     * It removes any occurrences of the regular expression <strong>-?maven-?</strong>,
137     * and then removes any occurrences of <strong>-?plugin-?</strong>.
138     * <p>
139     * For example, horsefeature-maven-plugin becomes horsefeature.
140     * </p>
141     * <p>
142     * (There is a special for maven-plugin-plugin; it is mapped to 'plugin'.
143     * </p>
144     *
145     * @since 2.4
146     */
147    @Parameter( property = "goalPrefix" )
148    protected String goalPrefix;
149
150    /**
151     * Set this to "true" to skip invoking any goals or reports of the plugin.
152     *
153     * @since 2.8
154     */
155    @Parameter( defaultValue = "false", property = "maven.plugin.skip" )
156    private boolean skip;
157
158    /**
159     * Set this to "true" to skip generating the report.
160     *
161     * @since 2.8
162     */
163    @Parameter( defaultValue = "false", property = "maven.plugin.report.skip" )
164    private boolean skipReport;
165
166    /**
167     * The set of dependencies for the current project
168     *
169     * @since 3.0
170     */
171    @Parameter( defaultValue = "${project.artifacts}", required = true, readonly = true )
172    protected Set<Artifact> dependencies;
173
174    /**
175     * List of Remote Repositories used by the resolver
176     *
177     * @since 3.0
178     */
179    @Parameter( defaultValue = "${project.remoteArtifactRepositories}", required = true, readonly = true )
180    protected List<ArtifactRepository> remoteRepos;
181
182    /**
183     * Location of the local repository.
184     *
185     * @since 3.0
186     */
187    @Parameter( defaultValue = "${localRepository}", required = true, readonly = true )
188    protected ArtifactRepository local;
189
190    /**
191     * {@inheritDoc}
192     */
193    protected Renderer getSiteRenderer()
194    {
195        return siteRenderer;
196    }
197
198    /**
199     * {@inheritDoc}
200     */
201    protected String getOutputDirectory()
202    {
203        // PLUGIN-191: output directory of plugin.html, not *-mojo.xml
204        return project.getReporting().getOutputDirectory();
205    }
206
207    /**
208     * {@inheritDoc}
209     */
210    protected MavenProject getProject()
211    {
212        return project;
213    }
214
215    /**
216     * {@inheritDoc}
217     */
218    public boolean canGenerateReport()
219    {
220        return "maven-plugin".equals( project.getPackaging() );
221    }
222
223    /**
224     * {@inheritDoc}
225     */
226    protected void executeReport( Locale locale )
227        throws MavenReportException
228    {
229        if ( !canGenerateReport() )
230        {
231            return;
232        }
233        if ( skip || skipReport )
234        {
235            getLog().info( "Maven Plugin Plugin Report generation skipped." );
236            return;
237        }
238
239        PluginDescriptor pluginDescriptor = extractPluginDescriptor();
240
241        // Generate the mojos' documentation
242        generateMojosDocumentation( pluginDescriptor, locale );
243
244        // Write the overview
245        PluginOverviewRenderer r =
246            new PluginOverviewRenderer( project, requirements, getSink(), pluginDescriptor, locale );
247        r.render();
248    }
249
250    private PluginDescriptor extractPluginDescriptor()
251        throws MavenReportException
252    {
253        PluginDescriptorBuilder builder = new PluginDescriptorBuilder();
254        try
255        {
256            return builder.build( new FileReader( new File( project.getBuild().getOutputDirectory(),
257                                                            "META-INF/maven/plugin.xml" ) ) );
258        }
259        catch ( FileNotFoundException e )
260        {
261            getLog().debug( "Failed to read META-INF/maven/plugin.xml, fall back to mojoScanner" );
262        }
263        catch ( PlexusConfigurationException e )
264        {
265            getLog().debug( "Failed to read META-INF/maven/plugin.xml, fall back to mojoScanner" );
266        }
267
268        // Copy from AbstractGeneratorMojo#execute()
269        String defaultGoalPrefix = PluginDescriptor.getGoalPrefixFromArtifactId( project.getArtifactId() );
270        if ( goalPrefix == null )
271        {
272            goalPrefix = defaultGoalPrefix;
273        }
274        else
275        {
276            getLog().warn( "\n\nGoal prefix is specified as: '" + goalPrefix + "'. Maven currently expects it to be '"
277                               + defaultGoalPrefix + "'.\n" );
278        }
279
280        // TODO: could use this more, eg in the writing of the plugin descriptor!
281        PluginDescriptor pluginDescriptor = new PluginDescriptor();
282
283        pluginDescriptor.setGroupId( project.getGroupId() );
284
285        pluginDescriptor.setArtifactId( project.getArtifactId() );
286
287        pluginDescriptor.setVersion( project.getVersion() );
288
289        pluginDescriptor.setGoalPrefix( goalPrefix );
290
291        try
292        {
293            @SuppressWarnings( "unchecked" )
294            List<ComponentDependency> deps = GeneratorUtils.toComponentDependencies( project.getRuntimeDependencies() );
295            pluginDescriptor.setDependencies( deps );
296
297            PluginToolsRequest request = new DefaultPluginToolsRequest( project, pluginDescriptor );
298            request.setEncoding( encoding );
299            request.setSkipErrorNoDescriptorsFound( true );
300            request.setDependencies( dependencies );
301            request.setLocal( this.local );
302            request.setRemoteRepos( this.remoteRepos );
303
304            try
305            {
306                mojoScanner.populatePluginDescriptor( request );
307            }
308            catch ( InvalidPluginDescriptorException e )
309            {
310                // this is OK, it happens to lifecycle plugins. Allow generation to proceed.
311                getLog().debug( "Plugin without mojos.", e );
312            }
313        }
314        catch ( ExtractionException e )
315        {
316            throw new MavenReportException( "Error extracting plugin descriptor: \'" + e.getLocalizedMessage() + "\'",
317                                            e );
318        }
319        return pluginDescriptor;
320    }
321
322    /**
323     * {@inheritDoc}
324     */
325    public String getDescription( Locale locale )
326    {
327        return getBundle( locale ).getString( "report.plugin.description" );
328    }
329
330    /**
331     * {@inheritDoc}
332     */
333    public String getName( Locale locale )
334    {
335        return getBundle( locale ).getString( "report.plugin.name" );
336    }
337
338    /**
339     * {@inheritDoc}
340     */
341    public String getOutputName()
342    {
343        return "plugin-info";
344    }
345
346    /**
347     * Generate the mojos documentation, as xdoc files.
348     *
349     * @param pluginDescriptor not null
350     * @param locale           not null
351     * @throws MavenReportException if any
352     */
353    private void generateMojosDocumentation( PluginDescriptor pluginDescriptor, Locale locale )
354        throws MavenReportException
355    {
356        try
357        {
358            File outputDir = outputDirectory;
359            outputDir.mkdirs();
360
361            PluginXdocGenerator generator = new PluginXdocGenerator( project, locale );
362            PluginToolsRequest pluginToolsRequest = new DefaultPluginToolsRequest( project, pluginDescriptor );
363            generator.execute( outputDir, pluginToolsRequest );
364        }
365        catch ( GeneratorException e )
366        {
367            throw new MavenReportException( "Error writing plugin documentation", e );
368        }
369
370    }
371
372    /**
373     * @param locale not null
374     * @return the bundle for this report
375     */
376    protected static ResourceBundle getBundle( Locale locale )
377    {
378        return ResourceBundle.getBundle( "plugin-report", locale, PluginReport.class.getClassLoader() );
379    }
380
381    /**
382     * Generates an overview page with the list of goals
383     * and a link to the goal's page.
384     */
385    static class PluginOverviewRenderer
386        extends AbstractMavenReportRenderer
387    {
388        private final MavenProject project;
389
390        private final Requirements requirements;
391
392        private final PluginDescriptor pluginDescriptor;
393
394        private final Locale locale;
395
396        /**
397         * @param project          not null
398         * @param requirements     not null
399         * @param sink             not null
400         * @param pluginDescriptor not null
401         * @param locale           not null
402         */
403        public PluginOverviewRenderer( MavenProject project, Requirements requirements, Sink sink,
404                                       PluginDescriptor pluginDescriptor, Locale locale )
405        {
406            super( sink );
407
408            this.project = project;
409
410            this.requirements = ( requirements == null ? new Requirements() : requirements );
411
412            this.pluginDescriptor = pluginDescriptor;
413
414            this.locale = locale;
415        }
416
417        /**
418         * {@inheritDoc}
419         */
420        public String getTitle()
421        {
422            return getBundle( locale ).getString( "report.plugin.title" );
423        }
424
425        /**
426         * {@inheritDoc}
427         */
428        @SuppressWarnings( { "unchecked", "rawtypes" } )
429        public void renderBody()
430        {
431            startSection( getTitle() );
432
433            if ( !( pluginDescriptor.getMojos() != null && pluginDescriptor.getMojos().size() > 0 ) )
434            {
435                paragraph( getBundle( locale ).getString( "report.plugin.goals.nogoal" ) );
436                endSection();
437                return;
438            }
439
440            paragraph( getBundle( locale ).getString( "report.plugin.goals.intro" ) );
441
442            boolean hasMavenReport = false;
443            for ( Iterator<MojoDescriptor> i = pluginDescriptor.getMojos().iterator(); i.hasNext(); )
444            {
445                MojoDescriptor mojo = i.next();
446
447                if ( GeneratorUtils.isMavenReport( mojo.getImplementation(), project ) )
448                {
449                    hasMavenReport = true;
450                }
451            }
452
453            startTable();
454
455            String goalColumnName = getBundle( locale ).getString( "report.plugin.goals.column.goal" );
456            String isMavenReport = getBundle( locale ).getString( "report.plugin.goals.column.isMavenReport" );
457            String descriptionColumnName = getBundle( locale ).getString( "report.plugin.goals.column.description" );
458            if ( hasMavenReport )
459            {
460                tableHeader( new String[]{ goalColumnName, isMavenReport, descriptionColumnName } );
461            }
462            else
463            {
464                tableHeader( new String[]{ goalColumnName, descriptionColumnName } );
465            }
466
467            List<MojoDescriptor> mojos = new ArrayList<MojoDescriptor>();
468            mojos.addAll( pluginDescriptor.getMojos() );
469            PluginUtils.sortMojos( mojos );
470            for ( MojoDescriptor mojo : mojos )
471            {
472                String goalName = mojo.getFullGoalName();
473
474                /*
475                 * Added ./ to define a relative path
476                 * @see AbstractMavenReportRenderer#getValidHref(java.lang.String)
477                 */
478                String goalDocumentationLink = "./" + mojo.getGoal() + "-mojo.html";
479
480                String description;
481                if ( StringUtils.isNotEmpty( mojo.getDeprecated() ) )
482                {
483                    description =
484                        "<strong>" + getBundle( locale ).getString( "report.plugin.goal.deprecated" ) + "</strong> "
485                            + GeneratorUtils.makeHtmlValid( mojo.getDeprecated() );
486                }
487                else if ( StringUtils.isNotEmpty( mojo.getDescription() ) )
488                {
489                    description = GeneratorUtils.makeHtmlValid( mojo.getDescription() );
490                }
491                else
492                {
493                    description = getBundle( locale ).getString( "report.plugin.goal.nodescription" );
494                }
495
496                sink.tableRow();
497                tableCell( createLinkPatternedText( goalName, goalDocumentationLink ) );
498                if ( hasMavenReport )
499                {
500                    if ( GeneratorUtils.isMavenReport( mojo.getImplementation(), project ) )
501                    {
502                        sink.tableCell();
503                        sink.text( getBundle( locale ).getString( "report.plugin.isReport" ) );
504                        sink.tableCell_();
505                    }
506                    else
507                    {
508                        sink.tableCell();
509                        sink.text( getBundle( locale ).getString( "report.plugin.isNotReport" ) );
510                        sink.tableCell_();
511                    }
512                }
513                tableCell( description, true );
514                sink.tableRow_();
515            }
516
517            endTable();
518
519            startSection( getBundle( locale ).getString( "report.plugin.systemrequirements" ) );
520
521            paragraph( getBundle( locale ).getString( "report.plugin.systemrequirements.intro" ) );
522
523            startTable();
524
525            String maven = discoverMavenRequirement( project, requirements );
526            sink.tableRow();
527            tableCell( getBundle( locale ).getString( "report.plugin.systemrequirements.maven" ) );
528            tableCell( ( maven != null
529                ? maven
530                : getBundle( locale ).getString( "report.plugin.systemrequirements.nominimum" ) ) );
531            sink.tableRow_();
532
533            String jdk = discoverJdkRequirement( project, requirements );
534            sink.tableRow();
535            tableCell( getBundle( locale ).getString( "report.plugin.systemrequirements.jdk" ) );
536            tableCell(
537                ( jdk != null ? jdk : getBundle( locale ).getString( "report.plugin.systemrequirements.nominimum" ) ) );
538            sink.tableRow_();
539
540            sink.tableRow();
541            tableCell( getBundle( locale ).getString( "report.plugin.systemrequirements.memory" ) );
542            tableCell( ( StringUtils.isNotEmpty( requirements.getMemory() )
543                ? requirements.getMemory()
544                : getBundle( locale ).getString( "report.plugin.systemrequirements.nominimum" ) ) );
545            sink.tableRow_();
546
547            sink.tableRow();
548            tableCell( getBundle( locale ).getString( "report.plugin.systemrequirements.diskspace" ) );
549            tableCell( ( StringUtils.isNotEmpty( requirements.getDiskSpace() )
550                ? requirements.getDiskSpace()
551                : getBundle( locale ).getString( "report.plugin.systemrequirements.nominimum" ) ) );
552            sink.tableRow_();
553
554            if ( requirements.getOthers() != null && requirements.getOthers().size() > 0 )
555            {
556                for ( Iterator it = requirements.getOthers().keySet().iterator(); it.hasNext(); )
557                {
558                    String key = it.next().toString();
559
560                    sink.tableRow();
561                    tableCell( key );
562                    tableCell( ( StringUtils.isNotEmpty( requirements.getOthers().getProperty( key ) )
563                        ? requirements.getOthers().getProperty( key )
564                        : getBundle( locale ).getString( "report.plugin.systemrequirements.nominimum" ) ) );
565                    sink.tableRow_();
566                }
567            }
568            endTable();
569
570            endSection();
571
572            renderUsageSection( hasMavenReport );
573
574            endSection();
575        }
576
577        /**
578         * Render the section about the usage of the plugin.
579         *
580         * @param hasMavenReport If the plugin has a report or not
581         */
582        private void renderUsageSection( boolean hasMavenReport )
583        {
584            startSection( getBundle( locale ).getString( "report.plugin.usage" ) );
585
586            // Configuration
587            sink.paragraph();
588            text( getBundle( locale ).getString( "report.plugin.usage.intro" ) );
589            sink.paragraph_();
590
591            StringBuilder sb = new StringBuilder();
592            sb.append( "<project>" ).append( '\n' );
593            sb.append( "  ..." ).append( '\n' );
594            sb.append( "  <build>" ).append( '\n' );
595            sb.append(
596                "    <!-- " + getBundle( locale ).getString( "report.plugin.usage.pluginManagement" ) + " -->" ).append(
597                '\n' );
598            sb.append( "    <pluginManagement>" ).append( '\n' );
599            sb.append( "      <plugins>" ).append( '\n' );
600            sb.append( "        <plugin>" ).append( '\n' );
601            sb.append( "          <groupId>" ).append( pluginDescriptor.getGroupId() ).append( "</groupId>" ).append(
602                '\n' );
603            sb.append( "          <artifactId>" ).append( pluginDescriptor.getArtifactId() ).append(
604                "</artifactId>" ).append( '\n' );
605            sb.append( "          <version>" ).append( pluginDescriptor.getVersion() ).append( "</version>" ).append(
606                '\n' );
607            sb.append( "        </plugin>" ).append( '\n' );
608            sb.append( "        ..." ).append( '\n' );
609            sb.append( "      </plugins>" ).append( '\n' );
610            sb.append( "    </pluginManagement>" ).append( '\n' );
611            sb.append( "    <!-- " + getBundle( locale ).getString( "report.plugin.usage.plugins" ) + " -->" ).append(
612                '\n' );
613            sb.append( "    <plugins>" ).append( '\n' );
614            sb.append( "      <plugin>" ).append( '\n' );
615            sb.append( "        <groupId>" ).append( pluginDescriptor.getGroupId() ).append( "</groupId>" ).append(
616                '\n' );
617            sb.append( "        <artifactId>" ).append( pluginDescriptor.getArtifactId() ).append(
618                "</artifactId>" ).append( '\n' );
619            sb.append( "        <version>" ).append( pluginDescriptor.getVersion() ).append( "</version>" ).append(
620                '\n' );
621            sb.append( "      </plugin>" ).append( '\n' );
622            sb.append( "      ..." ).append( '\n' );
623            sb.append( "    </plugins>" ).append( '\n' );
624            sb.append( "  </build>" ).append( '\n' );
625
626            if ( hasMavenReport )
627            {
628                sb.append( "  ..." ).append( '\n' );
629                sb.append(
630                    "  <!-- " + getBundle( locale ).getString( "report.plugin.usage.reporting" ) + " -->" ).append(
631                    '\n' );
632                sb.append( "  <reporting>" ).append( '\n' );
633                sb.append( "    <plugins>" ).append( '\n' );
634                sb.append( "      <plugin>" ).append( '\n' );
635                sb.append( "        <groupId>" ).append( pluginDescriptor.getGroupId() ).append( "</groupId>" ).append(
636                    '\n' );
637                sb.append( "        <artifactId>" ).append( pluginDescriptor.getArtifactId() ).append(
638                    "</artifactId>" ).append( '\n' );
639                sb.append( "        <version>" ).append( pluginDescriptor.getVersion() ).append( "</version>" ).append(
640                    '\n' );
641                sb.append( "      </plugin>" ).append( '\n' );
642                sb.append( "      ..." ).append( '\n' );
643                sb.append( "    </plugins>" ).append( '\n' );
644                sb.append( "  </reporting>" ).append( '\n' );
645            }
646
647            sb.append( "  ..." ).append( '\n' );
648            sb.append( "</project>" ).append( '\n' );
649
650            verbatimText( sb.toString() );
651
652            sink.paragraph();
653            linkPatternedText( getBundle( locale ).getString( "report.plugin.configuration.end" ) );
654            sink.paragraph_();
655
656            endSection();
657        }
658
659        /**
660         * Try to lookup on the Maven prerequisites property.
661         * If not specified, uses the value defined by the user.
662         *
663         * @param project      not null
664         * @param requirements not null
665         * @return the Maven version
666         */
667        private static String discoverMavenRequirement( MavenProject project, Requirements requirements )
668        {
669            String maven = requirements.getMaven();
670            if ( maven == null )
671            {
672                maven = ( project.getPrerequisites() != null ? project.getPrerequisites().getMaven() : null );
673            }
674            if ( maven == null )
675            {
676                maven = "2.0";
677            }
678
679            return maven;
680        }
681
682        /**
683         * <ol>
684         * <li>use configured jdk requirement</li>
685         * <li>use <code>target</code> configuration of <code>org.apache.maven.plugins:maven-compiler-plugin</code></li>
686         * <li>use <code>target</code> configuration of <code>org.apache.maven.plugins:maven-compiler-plugin</code> in
687         * <code>pluginManagement</code></li>
688         * <li>use <code>maven.compiler.target</code> property</li>
689         * </ol>
690         *
691         * @param project      not null
692         * @param requirements not null
693         * @return the JDK version
694         */
695        private static String discoverJdkRequirement( MavenProject project, Requirements requirements )
696        {
697            String jdk = requirements.getJdk();
698
699            if ( jdk != null )
700            {
701                return jdk;
702            }
703
704            @SuppressWarnings( "unchecked" )
705            Plugin compiler = getCompilerPlugin( project.getBuild().getPluginsAsMap() );
706            if ( compiler == null )
707            {
708                compiler = getCompilerPlugin( project.getPluginManagement().getPluginsAsMap() );
709            }
710
711            jdk = getPluginParameter( compiler, "target" );
712            if ( jdk != null )
713            {
714                return jdk;
715            }
716
717            // default value
718            jdk = project.getProperties().getProperty( "maven.compiler.target" );
719            if ( jdk != null )
720            {
721                return jdk;
722            }
723
724            // return "1.5" by default?
725
726            String version = ( compiler == null ) ? null : compiler.getVersion();
727
728            if ( version != null )
729            {
730                return "Default target for maven-compiler-plugin version " + version;
731            }
732
733            return "Unknown";
734        }
735
736        private static Plugin getCompilerPlugin( Map<String, Object> pluginsAsMap )
737        {
738            return (Plugin) pluginsAsMap.get( "org.apache.maven.plugins:maven-compiler-plugin" );
739        }
740
741        private static String getPluginParameter( Plugin plugin, String parameter )
742        {
743            if ( plugin != null )
744            {
745                Xpp3Dom pluginConf = (Xpp3Dom) plugin.getConfiguration();
746
747                if ( pluginConf != null )
748                {
749                    Xpp3Dom target = pluginConf.getChild( parameter );
750
751                    if ( target != null )
752                    {
753                        return target.getValue();
754                    }
755                }
756            }
757
758            return null;
759        }
760    }
761}