Coverage Report - org.apache.maven.plugin.doap.DoapMojo
 
Classes in this File Line Coverage Branch Coverage Complexity
DoapMojo
31 %
142/453
25 %
59/236
6,577
DoapMojo$1
0 %
0/16
0 %
0/12
6,577
 
 1  
 package org.apache.maven.plugin.doap;
 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.IOException;
 24  
 import java.io.Writer;
 25  
 import java.net.MalformedURLException;
 26  
 import java.net.URL;
 27  
 import java.text.DateFormat;
 28  
 import java.text.ParseException;
 29  
 import java.text.SimpleDateFormat;
 30  
 import java.util.Collections;
 31  
 import java.util.Comparator;
 32  
 import java.util.Date;
 33  
 import java.util.Iterator;
 34  
 import java.util.List;
 35  
 import java.util.Locale;
 36  
 import java.util.TimeZone;
 37  
 
 38  
 import org.apache.maven.artifact.Artifact;
 39  
 import org.apache.maven.artifact.factory.ArtifactFactory;
 40  
 import org.apache.maven.artifact.repository.ArtifactRepository;
 41  
 import org.apache.maven.artifact.repository.metadata.ArtifactRepositoryMetadata;
 42  
 import org.apache.maven.artifact.repository.metadata.RepositoryMetadata;
 43  
 import org.apache.maven.artifact.repository.metadata.RepositoryMetadataManager;
 44  
 import org.apache.maven.artifact.repository.metadata.RepositoryMetadataResolutionException;
 45  
 import org.apache.maven.model.Contributor;
 46  
 import org.apache.maven.model.Developer;
 47  
 import org.apache.maven.model.License;
 48  
 import org.apache.maven.model.MailingList;
 49  
 import org.apache.maven.model.Scm;
 50  
 import org.apache.maven.plugin.AbstractMojo;
 51  
 import org.apache.maven.plugin.MojoExecutionException;
 52  
 import org.apache.maven.plugin.doap.options.ASFExtOptions;
 53  
 import org.apache.maven.plugin.doap.options.DoapOptions;
 54  
 import org.apache.maven.plugin.doap.options.Standard;
 55  
 import org.apache.maven.project.MavenProject;
 56  
 import org.apache.maven.scm.manager.NoSuchScmProviderException;
 57  
 import org.apache.maven.scm.manager.ScmManager;
 58  
 import org.apache.maven.scm.provider.cvslib.repository.CvsScmProviderRepository;
 59  
 import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository;
 60  
 import org.apache.maven.scm.repository.ScmRepository;
 61  
 import org.apache.maven.scm.repository.ScmRepositoryException;
 62  
 import org.codehaus.plexus.i18n.I18N;
 63  
 import org.codehaus.plexus.util.FileUtils;
 64  
 import org.codehaus.plexus.util.StringUtils;
 65  
 import org.codehaus.plexus.util.WriterFactory;
 66  
 import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
 67  
 import org.codehaus.plexus.util.xml.XMLWriter;
 68  
 import org.codehaus.plexus.util.xml.XmlWriterUtil;
 69  
 
 70  
 /**
 71  
  * Generate a <a href="http://usefulinc.com/ns/doap">Description of a Project (DOAP)</a>
 72  
  * file from the main information found in a POM.
 73  
  * <br/>
 74  
  * <b>Note</b>: The generated file is tailored for use by projects at
 75  
  * <a href="http://projects.apache.org/doap.html">Apache</a>.
 76  
  *
 77  
  * @author Jason van Zyl
 78  
  * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
 79  
  * @version $Id: org.apache.maven.plugin.doap.DoapMojo.html 815332 2012-05-01 21:25:03Z hboutemy $
 80  
  * @since 1.0-beta-1
 81  
  * @goal generate
 82  
  */
 83  2
 public class DoapMojo
 84  
     extends AbstractMojo
 85  
 {
 86  
     /** UTC Time Zone */
 87  1
     private static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone( "UTC" );
 88  
 
 89  
     /** Date format for <lastUpdated/> tag in the repository metadata, i.e.: yyyyMMddHHmmss */
 90  
     private static final DateFormat REPOSITORY_DATE_FORMAT;
 91  
 
 92  
     /** Date format for DOAP file, i.e. ISO-8601 YYYY-MM-DD */
 93  
     private static final DateFormat DOAP_DATE_FORMAT;
 94  
 
 95  
     static
 96  
     {
 97  1
         REPOSITORY_DATE_FORMAT = new SimpleDateFormat( "yyyyMMddHHmmss", Locale.ENGLISH );
 98  1
         REPOSITORY_DATE_FORMAT.setTimeZone( UTC_TIME_ZONE );
 99  
 
 100  1
         DOAP_DATE_FORMAT = new SimpleDateFormat( "yyyy-MM-dd", Locale.ENGLISH );
 101  1
         DOAP_DATE_FORMAT.setTimeZone( UTC_TIME_ZONE );
 102  1
     }
 103  
 
 104  
     // ----------------------------------------------------------------------
 105  
     // Mojo components
 106  
     // ----------------------------------------------------------------------
 107  
 
 108  
     /**
 109  
      * Maven SCM Manager.
 110  
      *
 111  
      * @parameter expression="${component.org.apache.maven.scm.manager.ScmManager}"
 112  
      * @required
 113  
      * @readonly
 114  
      * @since 1.0
 115  
      */
 116  
     private ScmManager scmManager;
 117  
 
 118  
     /**
 119  
      * Artifact factory.
 120  
      *
 121  
      * @component
 122  
      * @since 1.0
 123  
      */
 124  
     private ArtifactFactory artifactFactory;
 125  
 
 126  
     /**
 127  
      * Used to resolve artifacts.
 128  
      *
 129  
      * @component
 130  
      * @since 1.0
 131  
      */
 132  
     private RepositoryMetadataManager repositoryMetadataManager;
 133  
 
 134  
     /**
 135  
      * Internationalization component.
 136  
      *
 137  
      * @component
 138  
      * @since 1.0
 139  
      */
 140  
     private I18N i18n;
 141  
 
 142  
     // ----------------------------------------------------------------------
 143  
     // Mojo parameters
 144  
     // ----------------------------------------------------------------------
 145  
 
 146  
     /**
 147  
      * The POM from which information will be extracted to create a DOAP file.
 148  
      *
 149  
      * @parameter expression="${project}"
 150  
      * @required
 151  
      */
 152  
     private MavenProject project;
 153  
 
 154  
     /**
 155  
      * The name of the DOAP file that will be generated.
 156  
      *
 157  
      * @parameter expression="${doapFile}"
 158  
      * default-value="${project.reporting.outputDirectory}/doap_${project.artifactId}.rdf"
 159  
      * @required
 160  
      */
 161  
     private File doapFile;
 162  
 
 163  
     /**
 164  
      * The local repository where the artifacts are located.
 165  
      *
 166  
      * @parameter expression="${localRepository}"
 167  
      * @required
 168  
      * @readonly
 169  
      * @since 1.0
 170  
      */
 171  
     private ArtifactRepository localRepository;
 172  
 
 173  
     /**
 174  
      * The remote repositories where the artifacts are located.
 175  
      *
 176  
      * @parameter expression="${project.remoteArtifactRepositories}"
 177  
      * @required
 178  
      * @readonly
 179  
      * @since 1.0
 180  
      */
 181  
     private List remoteRepositories;
 182  
 
 183  
     // ----------------------------------------------------------------------
 184  
     // Doap options
 185  
     // ----------------------------------------------------------------------
 186  
 
 187  
     /**
 188  
      * The category which should be displayed in the DOAP file.
 189  
      *
 190  
      * @parameter expression="${category}"
 191  
      * @deprecated Since 1.0. Instead of, configure <code>category</code> property in <code>doapOptions</code>
 192  
      * parameter.
 193  
      */
 194  
     private String category;
 195  
 
 196  
     /**
 197  
      * The programming language which should be displayed in the DOAP file.
 198  
      *
 199  
      * @parameter expression="${language}"
 200  
      * @deprecated Since 1.0. Instead of, configure <code>programmingLanguage</code> property in
 201  
      * <code>doapOptions</code> parameter.
 202  
      */
 203  
     private String language;
 204  
 
 205  
     /**
 206  
      * Specific DOAP parameters, i.e. options that POM doesn't have any notions.
 207  
      * <br/>
 208  
      * Example:
 209  
      * <pre>
 210  
      * &lt;doapOptions&gt;
 211  
      * &nbsp;&nbsp;&lt;programmingLanguage&gt;java&lt;/programmingLanguage&gt;
 212  
      * &lt;/doapOptions&gt;
 213  
      * </pre>
 214  
      * <br/>
 215  
      * See <a href="./apidocs/org/apache/maven/plugin/doap/options/DoapOptions.html">Javadoc</a>
 216  
      * <br/>
 217  
      *
 218  
      * @parameter expression="${doapOptions}"
 219  
      * @since 1.0
 220  
      * @see <a href="http://usefulinc.com/ns/doap#">http://usefulinc.com/ns/doap#</a>
 221  
      */
 222  
     private DoapOptions doapOptions;
 223  
 
 224  
     /**
 225  
      * Specific ASF extensions parameters, i.e. options that POM doesn't have any notions but required by ASF DOAP
 226  
      * requirements.
 227  
      * <br/>
 228  
      * Example:
 229  
      * <pre>
 230  
      * &lt;asfExtOptions&gt;
 231  
      * &nbsp;&nbsp;&lt;included&gt;true&lt;/included&gt;
 232  
      * &nbsp;&nbsp;&lt;charter&gt;The mission of the Apache XXX project is to create and maintain software
 233  
      * &nbsp;&nbsp;libraries that provide ...&lt;/charter&gt;
 234  
      * &nbsp;&nbsp;...
 235  
      * &lt;/asfExtOptions&gt;
 236  
      * </pre>
 237  
      * <b>Note</b>: By default, <code>asfExtOptions/included</code> is set to <code>true</code> to include the ASF
 238  
      * extensions.
 239  
      * <br/>
 240  
      * See <a href="./apidocs/org/apache/maven/plugin/doap/options/ASFExtOptions.html">Javadoc</a>
 241  
      * <br/>
 242  
      *
 243  
      * @parameter expression="${asfExtOptions}"
 244  
      * @since 1.0
 245  
      * @see <a href="http://svn.apache.org/repos/asf/infrastructure/site-tools/trunk/projects/asfext">
 246  
      * http://svn.apache.org/repos/asf/infrastructure/site-tools/trunk/projects/asfext</a>
 247  
      * @see <a href="http://projects.apache.org/docs/pmc.html">http://projects.apache.org/docs/pmc.html</a>
 248  
      * @see <a href="http://projects.apache.org/docs/standards.html">http://projects.apache.org/docs/standards.html</a>
 249  
      */
 250  
     private ASFExtOptions asfExtOptions;
 251  
 
 252  
     /**
 253  
      * The value for the <code>xml:lang</code> attribute used by the <code>&lt;rdf:RDF/&gt;<code>,
 254  
      * <code>&lt;description/&gt;</code> and <code>&lt;shortdesc/&gt;</code> elements.
 255  
      * <br/>
 256  
      * POM doesn't have any notions about language.
 257  
      * <br/>
 258  
      * See <a href="http://www.w3.org/TR/REC-xml/#sec-lang-tag">http://www.w3.org/TR/REC-xml/#sec-lang-tag</a>
 259  
      * <br/>
 260  
      *
 261  
      * @parameter expression="${lang}" default-value="en"
 262  
      * @required
 263  
      * @since 1.0
 264  
      */
 265  
     private String lang;
 266  
 
 267  
     /**
 268  
      * The <code>about</code> URI-reference which should be displayed in the DOAP file.
 269  
      * Example:
 270  
      * <pre>
 271  
      * &lt;rdf:RDF&gt;
 272  
      * &nbsp;&nbsp;&lt;Project rdf:about="http://maven.apache.org/"&gt;
 273  
      * &nbsp;&nbsp;...
 274  
      * &nbsp;&nbsp;&lt;/Project&gt;
 275  
      * &lt;/rdf:RDF&gt;
 276  
      * </pre>
 277  
      * See <a href="http://www.w3.org/TR/1999/REC-rdf-syntax-19990222/#aboutAttr">
 278  
      * http://www.w3.org/TR/1999/REC-rdf-syntax-19990222/#aboutAttr</a>
 279  
      * <br/>
 280  
      *
 281  
      * @parameter expression="${about}" default-value="${project.url}"
 282  
      * @required
 283  
      * @since 1.0
 284  
      */
 285  
     private String about;
 286  
 
 287  
     // ----------------------------------------------------------------------
 288  
     // Public methods
 289  
     // ----------------------------------------------------------------------
 290  
 
 291  
     /** {@inheritDoc} */
 292  
     public void execute()
 293  
         throws MojoExecutionException
 294  
     {
 295  
         // ----------------------------------------------------------------------------
 296  
         // setup pretty print xml writer
 297  
         // ----------------------------------------------------------------------------
 298  
 
 299  
         Writer w;
 300  
         try
 301  
         {
 302  2
             if ( !doapFile.getParentFile().exists() )
 303  
             {
 304  0
                 FileUtils.mkdir( doapFile.getParentFile().getAbsolutePath() );
 305  
             }
 306  
 
 307  2
             w = WriterFactory.newXmlWriter( doapFile );
 308  
         }
 309  0
         catch ( IOException e )
 310  
         {
 311  0
             throw new MojoExecutionException( "Error creating DOAP file.", e );
 312  2
         }
 313  
 
 314  2
         if ( asfExtOptions.isIncluded() )
 315  
         {
 316  1
             getLog().info( "Generating an ASF DOAP file..." );
 317  
         }
 318  
         else
 319  
         {
 320  1
             getLog().info( "Generating a pure DOAP file..." );
 321  
         }
 322  
 
 323  2
         XMLWriter writer = new PrettyPrintXMLWriter( w, project.getModel().getModelEncoding(), null );
 324  
 
 325  
         // ----------------------------------------------------------------------------
 326  
         // Convert POM to DOAP
 327  
         // ----------------------------------------------------------------------------
 328  
 
 329  2
         DoapUtil.writeHeader( writer );
 330  
 
 331  
         // Heading
 332  2
         writer.startElement( "rdf:RDF" );
 333  2
         writer.addAttribute( "xml:lang", lang );
 334  2
         writer.addAttribute( "xmlns", "http://usefulinc.com/ns/doap#" );
 335  2
         writer.addAttribute( "xmlns:rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#" );
 336  2
         writer.addAttribute( "xmlns:foaf", "http://xmlns.com/foaf/0.1/" );
 337  2
         if ( asfExtOptions.isIncluded() )
 338  
         {
 339  1
             writer.addAttribute( "xmlns:asfext", ASFExtOptions.ASFEXT_NAMESPACE );
 340  
         }
 341  
 
 342  
         // Project
 343  2
         writer.startElement( "Project" );
 344  2
         if ( StringUtils.isNotEmpty( about ) )
 345  
         {
 346  2
             writer.addAttribute( "rdf:about", about );
 347  
         }
 348  
         else
 349  
         {
 350  0
             getLog().warn( "rdf:about should be required" );
 351  
         }
 352  
 
 353  
         // name
 354  2
         writeName( writer );
 355  
 
 356  
         // description
 357  2
         writeDescription( writer );
 358  
 
 359  
         // created
 360  2
         writeCreated( writer );
 361  
 
 362  
         // homepage and old-homepage
 363  2
         writeHomepage( writer );
 364  
 
 365  
         // licenses
 366  2
         writeLicenses( writer );
 367  
 
 368  
         // programming-language
 369  2
         writeProgrammingLanguage( writer );
 370  
 
 371  
         // category
 372  2
         writeCategory( writer );
 373  
 
 374  
         // os
 375  2
         writeOS( writer );
 376  
 
 377  
         // SCM
 378  2
         writeSourceRepositories( writer );
 379  
 
 380  
         // bug-database
 381  2
         writeBugDatabase( writer );
 382  
 
 383  
         // mailing list
 384  2
         writeMailingList( writer );
 385  
 
 386  
         // download-page and download-mirror
 387  2
         writeDownloadPage( writer );
 388  
 
 389  
         // screenshots
 390  2
         writeScreenshots( writer );
 391  
 
 392  
         // wiki
 393  2
         writeWiki( writer );
 394  
 
 395  
         // Releases
 396  2
         writeReleases( writer );
 397  
 
 398  
         // Developers
 399  2
         writeDevelopersOrContributors( writer, project.getDevelopers() );
 400  
 
 401  
         // Contributors
 402  2
         writeDevelopersOrContributors( writer, project.getContributors() );
 403  
 
 404  
         // ASFext
 405  2
         if ( asfExtOptions.isIncluded() )
 406  
         {
 407  1
             writeASFext( writer );
 408  
         }
 409  
 
 410  2
         writer.endElement(); // Project
 411  2
         writer.endElement(); // rdf:RDF
 412  
 
 413  
         try
 414  
         {
 415  2
             w.close();
 416  
         }
 417  0
         catch ( IOException e )
 418  
         {
 419  0
             throw new MojoExecutionException( "Error when closing the writer.", e );
 420  2
         }
 421  2
     }
 422  
 
 423  
     // ----------------------------------------------------------------------
 424  
     // Private methods
 425  
     // ----------------------------------------------------------------------
 426  
 
 427  
     /**
 428  
      * Write DOAP name.
 429  
      *
 430  
      * @param writer not null
 431  
      * @see <a href="http://usefulinc.com/ns/doap#name">http://usefulinc.com/ns/doap#name</a>
 432  
      */
 433  
     private void writeName( XMLWriter writer )
 434  
     {
 435  2
         if ( StringUtils.isEmpty( project.getName() ) )
 436  
         {
 437  0
             return;
 438  
         }
 439  
 
 440  2
         XmlWriterUtil.writeLineBreak( writer );
 441  2
         XmlWriterUtil.writeCommentText( writer, "A name of something.", 2 );
 442  
 
 443  2
         if ( asfExtOptions.isIncluded()
 444  
             && !project.getName().toLowerCase( Locale.ENGLISH ).trim().startsWith( "apache" ) )
 445  
         {
 446  1
             DoapUtil.writeRdfResourceElement( writer, "name", "Apache " + project.getName() );
 447  
         }
 448  
         else
 449  
         {
 450  1
             DoapUtil.writeRdfResourceElement( writer, "name", project.getName() );
 451  
         }
 452  2
     }
 453  
 
 454  
     /**
 455  
      * Write DOAP description.
 456  
      *
 457  
      * @param writer not null
 458  
      * @see <a href="http://usefulinc.com/ns/doap#description">http://usefulinc.com/ns/doap#description</a>
 459  
      * @see <a href="http://usefulinc.com/ns/doap#shortdesc">http://usefulinc.com/ns/doap#shortdesc</a>
 460  
      */
 461  
     private void writeDescription( XMLWriter writer )
 462  
     {
 463  2
         if ( StringUtils.isEmpty( project.getDescription() ) )
 464  
         {
 465  0
             return;
 466  
         }
 467  
 
 468  2
         XmlWriterUtil.writeLineBreak( writer );
 469  2
         XmlWriterUtil.writeCommentText( writer, "Plain text description of a project, of 2-4 sentences in length.", 2 );
 470  2
         DoapUtil.writeElement( writer, "description", project.getDescription(), lang );
 471  2
         if ( StringUtils.isNotEmpty( doapOptions.getShortdesc() ) )
 472  
         {
 473  0
             DoapUtil.writeElement( writer, "shortdesc", doapOptions.getShortdesc(), lang );
 474  
         }
 475  
         else
 476  
         {
 477  2
             DoapUtil.writeElement( writer, "shortdesc", project.getDescription(), lang );
 478  
         }
 479  2
     }
 480  
 
 481  
     /**
 482  
      * Write DOAP created.
 483  
      *
 484  
      * @param writer not null
 485  
      * @see <a href="http://usefulinc.com/ns/doap#created">http://usefulinc.com/ns/doap#created</a>
 486  
      */
 487  
     private void writeCreated( XMLWriter writer )
 488  
     {
 489  2
         if ( StringUtils.isEmpty( project.getInceptionYear() ) )
 490  
         {
 491  2
             return;
 492  
         }
 493  
 
 494  0
         XmlWriterUtil.writeLineBreak( writer );
 495  0
         XmlWriterUtil.writeCommentText( writer, "Date when something was created, in YYYY-MM-DD form. e.g. 2004-04-05",
 496  
                                         2 );
 497  0
         DoapUtil.writeElement( writer, "created", project.getInceptionYear() + "-01-01" );
 498  0
     }
 499  
 
 500  
     /**
 501  
      * Write DOAP homepage and old-homepage.
 502  
      *
 503  
      * @param writer not null
 504  
      * @see <a href="http://usefulinc.com/ns/doap#homepage">http://usefulinc.com/ns/doap#homepage</a>
 505  
      * @see <a href="http://usefulinc.com/ns/doap#old-homepage">http://usefulinc.com/ns/doap#old-homepage</a>
 506  
      */
 507  
     private void writeHomepage( XMLWriter writer )
 508  
     {
 509  2
         if ( StringUtils.isNotEmpty( project.getUrl() ) )
 510  
         {
 511  2
             XmlWriterUtil.writeLineBreak( writer );
 512  2
             XmlWriterUtil.writeCommentText( writer,
 513  
                                             "URL of a project's homepage, associated with exactly one project.", 2 );
 514  2
             DoapUtil.writeRdfResourceElement( writer, "homepage", project.getUrl() );
 515  
         }
 516  
 
 517  2
         if ( StringUtils.isNotEmpty( doapOptions.getOldHomepage() ) )
 518  
         {
 519  0
             XmlWriterUtil.writeLineBreak( writer );
 520  0
             XmlWriterUtil.writeCommentText(
 521  
                                             writer,
 522  
                                             "URL of a project's past homepage, associated with exactly one project.",
 523  
                                             2 );
 524  0
             DoapUtil.writeRdfResourceElement( writer, "old-homepage", doapOptions.getOldHomepage() );
 525  
         }
 526  2
     }
 527  
 
 528  
     /**
 529  
      * Write DOAP programming-language.
 530  
      *
 531  
      * @param writer not null
 532  
      * @see <a href="http://usefulinc.com/ns/doap#programming-language">
 533  
      * http://usefulinc.com/ns/doap#programming-language</a>
 534  
      */
 535  
     private void writeProgrammingLanguage( XMLWriter writer )
 536  
     {
 537  2
         if ( StringUtils.isEmpty( doapOptions.getProgrammingLanguage() ) && StringUtils.isEmpty( language ) )
 538  
         {
 539  0
             return;
 540  
         }
 541  
 
 542  2
         XmlWriterUtil.writeLineBreak( writer );
 543  2
         XmlWriterUtil.writeCommentText( writer, "Programming language.", 2 );
 544  
 
 545  2
         if ( StringUtils.isNotEmpty( language ) ) // backward compatible
 546  
         {
 547  0
             if ( asfExtOptions.isIncluded() && !ASFExtOptions.isProgrammingLanguageSupportedByASF( language ) )
 548  
             {
 549  0
                 getLog().warn(
 550  
                                "The programming language '" + language + "' is not supported by ASF. "
 551  
                                    + "Refer you to http://projects.apache.org/languages.html" );
 552  
             }
 553  
 
 554  0
             DoapUtil.writeRdfResourceElement( writer, "programming-language", language );
 555  
         }
 556  
 
 557  2
         if ( StringUtils.isNotEmpty( doapOptions.getProgrammingLanguage() ) )
 558  
         {
 559  2
             String[] languages = StringUtils.split( doapOptions.getProgrammingLanguage(), "," );
 560  4
             for ( int i = 0; i < languages.length; i++ )
 561  
             {
 562  2
                 if ( asfExtOptions.isIncluded()
 563  
                     && !ASFExtOptions.isProgrammingLanguageSupportedByASF( languages[i].trim() ) )
 564  
                 {
 565  0
                     getLog().warn(
 566  
                                    "The programming language '" + languages[i].trim() + "' is not supported by ASF. "
 567  
                                        + "Refer you to http://projects.apache.org/languages.html" );
 568  
                 }
 569  
 
 570  2
                 DoapUtil.writeRdfResourceElement( writer, "programming-language", languages[i].trim() );
 571  
             }
 572  
         }
 573  2
     }
 574  
 
 575  
     /**
 576  
      * Write DOAP category.
 577  
      *
 578  
      * @param writer not null
 579  
      * @see <a href="http://usefulinc.com/ns/doap#category">http://usefulinc.com/ns/doap#category</a>
 580  
      */
 581  
     private void writeCategory( XMLWriter writer )
 582  
     {
 583  2
         if ( StringUtils.isEmpty( doapOptions.getCategory() ) && StringUtils.isEmpty( category ) )
 584  
         {
 585  2
             return;
 586  
         }
 587  
 
 588  
         //TODO: how to lookup category, map it, or just declare it.
 589  0
         XmlWriterUtil.writeLineBreak( writer );
 590  0
         XmlWriterUtil.writeCommentText( writer, "A category of project.", 2 );
 591  
 
 592  0
         if ( StringUtils.isNotEmpty( category ) ) // backward compatible
 593  
         {
 594  0
             if ( asfExtOptions.isIncluded() && !ASFExtOptions.isCategorySupportedByASF( category ) )
 595  
             {
 596  0
                 getLog().warn(
 597  
                                "The given category '" + category + "' is not supported by ASF. "
 598  
                                    + "Refer you to http://projects.apache.org/categories.html" );
 599  
             }
 600  
 
 601  0
             if ( asfExtOptions.isIncluded() )
 602  
             {
 603  0
                 DoapUtil
 604  
                     .writeRdfResourceElement( writer, "category", "http://projects.apache.org/category/" + category );
 605  
             }
 606  
             else
 607  
             {
 608  0
                 DoapUtil.writeRdfResourceElement( writer, "category", category );
 609  
             }
 610  
         }
 611  
 
 612  0
         if ( StringUtils.isNotEmpty( doapOptions.getCategory() ) )
 613  
         {
 614  0
             String[] categories = StringUtils.split( doapOptions.getCategory(), "," );
 615  0
             for ( int i = 0; i < categories.length; i++ )
 616  
             {
 617  0
                 if ( asfExtOptions.isIncluded() && !ASFExtOptions.isCategorySupportedByASF( categories[i] ) )
 618  
                 {
 619  0
                     getLog().warn(
 620  
                                    "The given category '" + categories[i] + "' is not supported by ASF. "
 621  
                                        + "Refer you to http://projects.apache.org/categories.html" );
 622  
                 }
 623  
 
 624  0
                 if ( asfExtOptions.isIncluded() )
 625  
                 {
 626  0
                     DoapUtil.writeRdfResourceElement( writer, "category", "http://projects.apache.org/category/"
 627  
                         + categories[i].trim() );
 628  
                 }
 629  
                 else
 630  
                 {
 631  0
                     DoapUtil.writeRdfResourceElement( writer, "category", categories[i].trim() );
 632  
                 }
 633  
             }
 634  
         }
 635  0
     }
 636  
 
 637  
     /**
 638  
      * Write DOAP download-page and download-mirror.
 639  
      *
 640  
      * @param writer not null
 641  
      * @see <a href="http://usefulinc.com/ns/doap#download-page">http://usefulinc.com/ns/doap#download-page</a>
 642  
      * @see <a href="http://usefulinc.com/ns/doap#download-mirror">http://usefulinc.com/ns/doap#download-mirror</a>
 643  
      */
 644  
     private void writeDownloadPage( XMLWriter writer )
 645  
     {
 646  2
         if ( StringUtils.isEmpty( doapOptions.getDownloadPage() ) )
 647  
         {
 648  2
             if ( StringUtils.isNotEmpty( project.getUrl() ) )
 649  
             {
 650  2
                 doapOptions.setDownloadPage( composeUrl( project.getUrl(), "/download.html" ) );
 651  
             }
 652  
         }
 653  
 
 654  2
         if ( StringUtils.isNotEmpty( doapOptions.getDownloadPage() ) )
 655  
         {
 656  2
             XmlWriterUtil.writeLineBreak( writer );
 657  2
             XmlWriterUtil.writeCommentText( writer, "Download page.", 2 );
 658  2
             DoapUtil.writeRdfResourceElement( writer, "download-page", doapOptions.getDownloadPage() );
 659  
         }
 660  
 
 661  2
         if ( StringUtils.isNotEmpty( doapOptions.getDownloadMirror() ) )
 662  
         {
 663  0
             XmlWriterUtil.writeLineBreak( writer );
 664  0
             XmlWriterUtil.writeCommentText( writer, "Mirror of software download web page.", 2 );
 665  0
             String[] downloadMirrors = StringUtils.split( doapOptions.getDownloadMirror(), "," );
 666  0
             for ( int i = 0; i < downloadMirrors.length; i++ )
 667  
             {
 668  0
                 DoapUtil.writeRdfResourceElement( writer, "download-mirror", downloadMirrors[i].trim() );
 669  
             }
 670  
         }
 671  2
     }
 672  
 
 673  
     /**
 674  
      * Write DOAP OS.
 675  
      *
 676  
      * @param writer not null
 677  
      * @see <a href="http://usefulinc.com/ns/doap#os">http://usefulinc.com/ns/doap#os</a>
 678  
      */
 679  
     private void writeOS( XMLWriter writer )
 680  
     {
 681  2
         if ( StringUtils.isEmpty( doapOptions.getOs() ) )
 682  
         {
 683  2
             return;
 684  
         }
 685  
 
 686  0
         XmlWriterUtil.writeLineBreak( writer );
 687  0
         XmlWriterUtil.writeCommentText( writer, "Operating system that a project is limited to.", 2 );
 688  
 
 689  0
         String[] oses = StringUtils.split( doapOptions.getOs(), "," );
 690  0
         for ( int i = 0; i < oses.length; i++ )
 691  
         {
 692  0
             DoapUtil.writeRdfResourceElement( writer, "os", oses[i].trim() );
 693  
         }
 694  0
     }
 695  
 
 696  
     /**
 697  
      * Write DOAP screenshots.
 698  
      *
 699  
      * @param writer not null
 700  
      * @see <a href="http://usefulinc.com/ns/doap#screenshots">http://usefulinc.com/ns/doap#screenshots</a>
 701  
      */
 702  
     private void writeScreenshots( XMLWriter writer )
 703  
     {
 704  2
         if ( StringUtils.isEmpty( doapOptions.getScreenshots() ) )
 705  
         {
 706  2
             return;
 707  
         }
 708  
 
 709  0
         XmlWriterUtil.writeLineBreak( writer );
 710  0
         XmlWriterUtil.writeCommentText( writer, "Web page with screenshots of project.", 2 );
 711  0
         DoapUtil.writeRdfResourceElement( writer, "screenshots", doapOptions.getScreenshots() );
 712  0
     }
 713  
 
 714  
     /**
 715  
      * Write DOAP wiki.
 716  
      *
 717  
      * @param writer not null
 718  
      * @see <a href="http://usefulinc.com/ns/doap#wiki">http://usefulinc.com/ns/doap#wiki</a>
 719  
      */
 720  
     private void writeWiki( XMLWriter writer )
 721  
     {
 722  2
         if ( StringUtils.isEmpty( doapOptions.getWiki() ) )
 723  
         {
 724  2
             return;
 725  
         }
 726  
 
 727  0
         XmlWriterUtil.writeLineBreak( writer );
 728  0
         XmlWriterUtil.writeCommentText( writer, "URL of Wiki for collaborative discussion of project.", 2 );
 729  0
         DoapUtil.writeRdfResourceElement( writer, "wiki", doapOptions.getWiki() );
 730  0
     }
 731  
 
 732  
     /**
 733  
      * Write DOAP licenses.
 734  
      *
 735  
      * @param writer not null
 736  
      * @see <a href="http://usefulinc.com/ns/doap#license">http://usefulinc.com/ns/doap#license</a>
 737  
      */
 738  
     private void writeLicenses( XMLWriter writer )
 739  
     {
 740  2
         if ( project.getLicenses() == null || project.getLicenses().isEmpty() )
 741  
         {
 742  2
             return;
 743  
         }
 744  
 
 745  0
         XmlWriterUtil.writeLineBreak( writer );
 746  0
         XmlWriterUtil.writeCommentText( writer, "The URI of the license the software is distributed under.", 2 );
 747  
         //TODO: how to map to usefulinc site, or if this is necessary, the OSI page might
 748  
         //      be more appropriate.
 749  0
         for ( Iterator it = project.getLicenses().iterator(); it.hasNext(); )
 750  
         {
 751  0
             License license = (License) it.next();
 752  
 
 753  0
             if ( StringUtils.isNotEmpty( license.getUrl() ) )
 754  
             {
 755  0
                 DoapUtil.writeRdfResourceElement( writer, "license", license.getUrl() );
 756  
             }
 757  
             else
 758  
             {
 759  0
                 getLog().warn( "No URL was specified for license " + license.getName() );
 760  
             }
 761  0
         }
 762  0
     }
 763  
 
 764  
     /**
 765  
      * Write DOAP bug-database.
 766  
      *
 767  
      * @param writer not null
 768  
      * @see <a href="http://usefulinc.com/ns/doap#bug-database">http://usefulinc.com/ns/doap#bug-database</a>
 769  
      */
 770  
     private void writeBugDatabase( XMLWriter writer )
 771  
     {
 772  2
         if ( project.getIssueManagement() == null )
 773  
         {
 774  2
             return;
 775  
         }
 776  
 
 777  0
         XmlWriterUtil.writeLineBreak( writer );
 778  0
         XmlWriterUtil.writeCommentText( writer, "bug database.", 2 );
 779  0
         if ( StringUtils.isNotEmpty( project.getIssueManagement().getUrl() ) )
 780  
         {
 781  0
             DoapUtil.writeRdfResourceElement( writer, "bug-database", project.getIssueManagement().getUrl() );
 782  
         }
 783  
         else
 784  
         {
 785  0
             getLog().warn( "No URL was specified for issue management" );
 786  
         }
 787  0
     }
 788  
 
 789  
     /**
 790  
      * Write DOAP mailing-list.
 791  
      *
 792  
      * @param writer not null
 793  
      * @see <a href="http://usefulinc.com/ns/doap#mailing-list">http://usefulinc.com/ns/doap#mailing-list</a>
 794  
      */
 795  
     private void writeMailingList( XMLWriter writer )
 796  
     {
 797  2
         if ( project.getMailingLists() == null || project.getMailingLists().isEmpty() )
 798  
         {
 799  2
             return;
 800  
         }
 801  
 
 802  0
         XmlWriterUtil.writeLineBreak( writer );
 803  0
         XmlWriterUtil.writeCommentText( writer, "mailing list.", 2 );
 804  0
         for ( Iterator it = project.getMailingLists().iterator(); it.hasNext(); )
 805  
         {
 806  0
             MailingList mailingList = (MailingList) it.next();
 807  
 
 808  0
             if ( StringUtils.isNotEmpty( mailingList.getArchive() ) )
 809  
             {
 810  0
                 DoapUtil.writeRdfResourceElement( writer, "mailing-list", mailingList.getArchive() );
 811  
             }
 812  
             else
 813  
             {
 814  0
                 getLog().warn( "No archive was specified for mailing list " + mailingList.getName() );
 815  
             }
 816  
 
 817  0
             if ( mailingList.getOtherArchives() != null )
 818  
             {
 819  0
                 for ( Iterator it2 = mailingList.getOtherArchives().iterator(); it2.hasNext(); )
 820  
                 {
 821  0
                     String otherArchive = (String) it2.next();
 822  
 
 823  0
                     if ( StringUtils.isNotEmpty( otherArchive ) )
 824  
                     {
 825  0
                         DoapUtil.writeRdfResourceElement( writer, "mailing-list", otherArchive );
 826  
                     }
 827  
                     else
 828  
                     {
 829  0
                         getLog().warn( "No other archive was specified for mailing list " + mailingList.getName() );
 830  
                     }
 831  0
                 }
 832  
             }
 833  0
         }
 834  0
     }
 835  
 
 836  
     /**
 837  
      * Write all DOAP releases.
 838  
      *
 839  
      * @param writer not null
 840  
      * @throws MojoExecutionException if any
 841  
      * @see <a href="http://usefulinc.com/ns/doap#release">http://usefulinc.com/ns/doap#release</a>
 842  
      * @see <a href="http://usefulinc.com/ns/doap#Version">http://usefulinc.com/ns/doap#Version</a>
 843  
      */
 844  
     private void writeReleases( XMLWriter writer )
 845  
         throws MojoExecutionException
 846  
     {
 847  2
         Artifact artifact = artifactFactory.createArtifact( project.getGroupId(), project.getArtifactId(), project
 848  
             .getVersion(), null, project.getPackaging() );
 849  2
         RepositoryMetadata metadata = new ArtifactRepositoryMetadata( artifact );
 850  
 
 851  2
         for ( Iterator it = remoteRepositories.iterator(); it.hasNext(); )
 852  
         {
 853  2
             ArtifactRepository repo = (ArtifactRepository) it.next();
 854  
 
 855  2
             if ( repo.isBlacklisted() )
 856  
             {
 857  0
                 continue;
 858  
             }
 859  2
             if ( repo.getSnapshots().isEnabled() )
 860  
             {
 861  2
                 continue;
 862  
             }
 863  0
             if ( repo.getReleases().isEnabled() )
 864  
             {
 865  
                 try
 866  
                 {
 867  0
                     repositoryMetadataManager.resolveAlways( metadata, localRepository, repo );
 868  0
                     break;
 869  
                 }
 870  0
                 catch ( RepositoryMetadataResolutionException e )
 871  
                 {
 872  0
                     throw new MojoExecutionException( metadata
 873  
                         + " could not be retrieved from repositories due to an error: " + e.getMessage(), e );
 874  
                 }
 875  
             }
 876  0
         }
 877  
 
 878  2
         if ( metadata.getMetadata().getVersioning() == null )
 879  
         {
 880  2
             getLog().info( "No versioning was found - ignored writing <release/> tag." );
 881  2
             return;
 882  
         }
 883  
 
 884  0
         List versions = metadata.getMetadata().getVersioning().getVersions();
 885  
 
 886  
         // Recent releases in first
 887  0
         Collections.reverse( versions );
 888  0
         boolean addComment = false;
 889  0
         int i = 0;
 890  0
         for ( Iterator it = versions.iterator(); it.hasNext(); )
 891  
         {
 892  0
             String version = (String) it.next();
 893  
 
 894  0
             if ( !addComment )
 895  
             {
 896  0
                 XmlWriterUtil.writeLineBreak( writer );
 897  0
                 XmlWriterUtil.writeCommentText( writer, "Project releases.", 2 );
 898  0
                 addComment = true;
 899  
             }
 900  
 
 901  0
             writer.startElement( "release" );
 902  0
             writer.startElement( "Version" );
 903  
 
 904  0
             writer.startElement( "name" );
 905  0
             if ( version.equals( metadata.getMetadata().getVersioning().getRelease() ) )
 906  
             {
 907  0
                 writer.writeText( "Latest stable release" );
 908  
             }
 909  
             else
 910  
             {
 911  0
                 writer.writeText( project.getName() + " - " + version );
 912  
             }
 913  0
             writer.endElement(); // name
 914  
 
 915  0
             writer.startElement( "revision" );
 916  0
             writer.writeText( version );
 917  0
             writer.endElement(); // revision
 918  
 
 919  
             // list all file release from all remote repos
 920  0
             for ( Iterator it2 = remoteRepositories.iterator(); it2.hasNext(); )
 921  
             {
 922  0
                 ArtifactRepository repo = (ArtifactRepository) it2.next();
 923  
 
 924  0
                 Artifact artifactRelease = artifactFactory.createArtifact( project.getGroupId(), project
 925  
                     .getArtifactId(), version, null, project.getPackaging() );
 926  
 
 927  0
                 if ( artifactRelease == null )
 928  
                 {
 929  0
                     continue;
 930  
                 }
 931  
 
 932  0
                 String fileRelease = repo.getUrl() + "/" + repo.pathOf( artifactRelease );
 933  
                 // try to ping the url
 934  
                 try
 935  
                 {
 936  0
                     URL urlRelease = new URL( fileRelease );
 937  0
                     urlRelease.openStream();
 938  
                 }
 939  0
                 catch ( MalformedURLException e )
 940  
                 {
 941  0
                     getLog().debug( e.getMessage(), e );
 942  0
                     continue;
 943  
                 }
 944  0
                 catch ( IOException e )
 945  
                 {
 946  
                     // Not found, ignored
 947  0
                     getLog().debug( e.getMessage(), e );
 948  0
                     continue;
 949  0
                 }
 950  
 
 951  0
                 writer.startElement( "file-release" );
 952  0
                 writer.writeText( fileRelease );
 953  0
                 writer.endElement(); // file-release
 954  
 
 955  0
                 Date releaseDate = null;
 956  
                 try
 957  
                 {
 958  0
                     releaseDate =
 959  
                         REPOSITORY_DATE_FORMAT.parse( metadata.getMetadata().getVersioning().getLastUpdated() );
 960  
                 }
 961  0
                 catch ( ParseException e )
 962  
                 {
 963  0
                     getLog().error(
 964  
                                     "Unable to parse date '"
 965  
                                         + metadata.getMetadata().getVersioning().getLastUpdated() + "'" );
 966  0
                     continue;
 967  0
                 }
 968  
 
 969  
                 // See MDOAP-11
 970  0
                 if ( i == 0 )
 971  
                 {
 972  0
                     writer.startElement( "created" );
 973  0
                     writer.writeText( DOAP_DATE_FORMAT.format( releaseDate ) );
 974  0
                     writer.endElement(); // created
 975  
                 }
 976  0
             }
 977  
 
 978  0
             writer.endElement(); // Version
 979  0
             writer.endElement(); // release
 980  
 
 981  0
             i++;
 982  0
         }
 983  0
     }
 984  
 
 985  
     /**
 986  
      * Write all DOAP repositories.
 987  
      *
 988  
      * @param writer not null
 989  
      * @see <a href="http://usefulinc.com/ns/doap#Repository">http://usefulinc.com/ns/doap#Repository</a>
 990  
      * @see <a href="http://usefulinc.com/ns/doap#CVSRepository">http://usefulinc.com/ns/doap#CVSRepository</a>
 991  
      * @see <a href="http://usefulinc.com/ns/doap#SVNRepository">http://usefulinc.com/ns/doap#SVNRepository</a>
 992  
      */
 993  
     private void writeSourceRepositories( XMLWriter writer )
 994  
     {
 995  2
         Scm scm = project.getScm();
 996  2
         if ( scm == null )
 997  
         {
 998  2
             return;
 999  
         }
 1000  
 
 1001  0
         XmlWriterUtil.writeLineBreak( writer );
 1002  0
         XmlWriterUtil.writeCommentText( writer, "Anonymous Source Repository", 2 );
 1003  0
         String anonymousConnection = scm.getConnection();
 1004  0
         writeSourceRepository( writer, anonymousConnection );
 1005  
 
 1006  0
         XmlWriterUtil.writeLineBreak( writer );
 1007  0
         XmlWriterUtil.writeCommentText( writer, "Developer Source Repository", 2 );
 1008  0
         String developerConnection = scm.getDeveloperConnection();
 1009  0
         writeSourceRepository( writer, developerConnection );
 1010  0
     }
 1011  
 
 1012  
     /**
 1013  
      * Write a DOAP repository, for instance:
 1014  
      * <pre>
 1015  
      *   &lt;repository&gt;
 1016  
      *     &lt;SVNRepository&gt;
 1017  
      *       &lt;location rdf:resource="http://svn.apache.org/repos/asf/maven/components/trunk/"/&gt;
 1018  
      *       &lt;browse rdf:resource="http://svn.apache.org/viewcvs.cgi/maven/components/trunk/"/&gt;
 1019  
      *     &lt;/SVNRepository&gt;
 1020  
      *   &lt;/repository&gt;
 1021  
      * </pre>
 1022  
      *
 1023  
      * @param writer not null
 1024  
      * @param connection not null
 1025  
      * @see <a href="http://usefulinc.com/ns/doap#Repository">http://usefulinc.com/ns/doap#Repository</a>
 1026  
      * @see <a href="http://usefulinc.com/ns/doap#CVSRepository">http://usefulinc.com/ns/doap#CVSRepository</a>
 1027  
      * @see <a href="http://usefulinc.com/ns/doap#SVNRepository">http://usefulinc.com/ns/doap#SVNRepository</a>
 1028  
      */
 1029  
     private void writeSourceRepository( XMLWriter writer, String connection )
 1030  
     {
 1031  0
         ScmRepository repository = getScmRepository( connection );
 1032  
 
 1033  0
         writer.startElement( "repository" );
 1034  
 
 1035  0
         if ( isScmSystem( repository, "cvs" ) )
 1036  
         {
 1037  0
             writer.startElement( "CVSRepository" );
 1038  
 
 1039  0
             CvsScmProviderRepository cvsRepo = (CvsScmProviderRepository) repository.getProviderRepository();
 1040  
 
 1041  0
             DoapUtil.writeElement( writer, "anon-root", cvsRepo.getCvsRoot() );
 1042  0
             DoapUtil.writeElement( writer, "module", cvsRepo.getModule() );
 1043  0
         }
 1044  0
         else if ( isScmSystem( repository, "svn" ) )
 1045  
         {
 1046  0
             writer.startElement( "SVNRepository" );
 1047  
 
 1048  0
             SvnScmProviderRepository svnRepo = (SvnScmProviderRepository) repository.getProviderRepository();
 1049  
 
 1050  0
             DoapUtil.writeRdfResourceElement( writer, "location", svnRepo.getUrl() );
 1051  0
         }
 1052  
         else
 1053  
         {
 1054  
             /*
 1055  
              * Supported DOAP repositories actually unsupported by SCM:
 1056  
              *   BitKeeper (http://usefulinc.com/ns/doap#BKRepository)
 1057  
              *   Arch (http://usefulinc.com/ns/doap#ArchRepository)
 1058  
              * Other SCM repos are unsupported by DOAP.
 1059  
              */
 1060  0
             writer.startElement( "Repository" );
 1061  
 
 1062  0
             if ( connection.length() < 4 )
 1063  
             {
 1064  0
                 throw new IllegalArgumentException( "The source repository connection is too short." );
 1065  
             }
 1066  
 
 1067  0
             DoapUtil.writeRdfResourceElement( writer, "location", connection.substring( 4 ) );
 1068  
         }
 1069  
 
 1070  0
         DoapUtil.writeRdfResourceElement( writer, "browse", project.getScm().getUrl() );
 1071  
 
 1072  0
         writer.endElement(); // CVSRepository || SVNRepository || Repository
 1073  0
         writer.endElement(); // repository
 1074  0
     }
 1075  
 
 1076  
     /**
 1077  
      * Write all DOAP persons.
 1078  
      *
 1079  
      * @param writer not null
 1080  
      * @param developersOrContributors list of developers or contributors
 1081  
      */
 1082  
     private void writeDevelopersOrContributors( XMLWriter writer, List developersOrContributors )
 1083  
     {
 1084  4
         if ( developersOrContributors == null || developersOrContributors.isEmpty() )
 1085  
         {
 1086  4
             return;
 1087  
         }
 1088  
 
 1089  0
         boolean isDeveloper = Developer.class.isAssignableFrom( developersOrContributors.get( 0 ).getClass() );
 1090  0
         if ( isDeveloper )
 1091  
         {
 1092  0
             XmlWriterUtil.writeLineBreak( writer );
 1093  0
             XmlWriterUtil.writeCommentText( writer, "Main committers", 2 );
 1094  
         }
 1095  
         else
 1096  
         {
 1097  0
             XmlWriterUtil.writeLineBreak( writer );
 1098  0
             XmlWriterUtil.writeCommentText( writer, "Contributed persons", 2 );
 1099  
         }
 1100  
 
 1101  0
         List maintainers =  DoapUtil.getDevelopersOrContributorsWithMaintainerRole( i18n, developersOrContributors );
 1102  0
         List developers = DoapUtil.getDevelopersOrContributorsWithDeveloperRole( i18n, developersOrContributors );
 1103  0
         List documenters = DoapUtil.getDevelopersOrContributorsWithDocumenterRole( i18n, developersOrContributors );
 1104  0
         List translators = DoapUtil.getDevelopersOrContributorsWithTranslatorRole( i18n, developersOrContributors );
 1105  0
         List testers = DoapUtil.getDevelopersOrContributorsWithTesterRole( i18n, developersOrContributors );
 1106  0
         List helpers = DoapUtil.getDevelopersOrContributorsWithHelperRole( i18n, developersOrContributors );
 1107  0
         List unknowns = DoapUtil.getDevelopersOrContributorsWithUnknownRole( i18n, developersOrContributors );
 1108  
 
 1109  
         // By default, all developers are maintainers and contributors are helpers
 1110  0
         if ( isDeveloper )
 1111  
         {
 1112  0
             maintainers.addAll( unknowns );
 1113  
         }
 1114  
         else
 1115  
         {
 1116  0
             helpers.addAll( unknowns );
 1117  
         }
 1118  
 
 1119  
         // all alphabetical
 1120  0
         if ( developers.size() != 0 )
 1121  
         {
 1122  0
             writeDeveloperOrContributor( writer, developers, "developer" );
 1123  
         }
 1124  0
         if ( documenters.size() != 0 )
 1125  
         {
 1126  0
             writeDeveloperOrContributor( writer, documenters, "documenter" );
 1127  
         }
 1128  0
         if ( helpers.size() != 0 )
 1129  
         {
 1130  0
             writeDeveloperOrContributor( writer, helpers, "helper" );
 1131  
         }
 1132  0
         if ( maintainers.size() != 0 )
 1133  
         {
 1134  0
             writeDeveloperOrContributor( writer, maintainers, "maintainer" );
 1135  
         }
 1136  0
         if ( testers.size() != 0 )
 1137  
         {
 1138  0
             writeDeveloperOrContributor( writer, testers, "tester" );
 1139  
         }
 1140  0
         if ( translators.size() != 0 )
 1141  
         {
 1142  0
             writeDeveloperOrContributor( writer, translators, "translator" );
 1143  
         }
 1144  0
     }
 1145  
 
 1146  
     /**
 1147  
      * Write a DOAP maintainer or developer or documenter or translator or tester or helper, for instance:
 1148  
      * <pre>
 1149  
      *   &lt;maintainer&gt;
 1150  
      *     &lt;foaf:Person&gt;
 1151  
      *       &lt;foaf:name&gt;Emmanuel Venisse&lt;/foaf:name&gt;
 1152  
      *       &lt;foaf:mbox rdf:resource="mailto:evenisse@apache.org"/&gt;
 1153  
      *     &lt;/foaf:Person&gt;
 1154  
      *   &lt;/maintainer&gt;
 1155  
      * </pre>
 1156  
      *
 1157  
      * @param writer not null
 1158  
      * @param developersOrContributors list of <code>{@link Developer}/{@link Contributor}</code>
 1159  
      * @param doapType not null
 1160  
      * @see #writeDeveloperOrContributor(XMLWriter, Object, String)
 1161  
      */
 1162  
     private void writeDeveloperOrContributor( XMLWriter writer, List developersOrContributors, String doapType )
 1163  
     {
 1164  0
         if ( developersOrContributors == null || developersOrContributors.isEmpty() )
 1165  
         {
 1166  0
             return;
 1167  
         }
 1168  
 
 1169  
         // Sort list by names
 1170  0
         Collections.sort( developersOrContributors, new Comparator()
 1171  
         {
 1172  
             /**
 1173  
              * {@inheritDoc}
 1174  
              */
 1175  0
             public int compare( Object arg0, Object arg1 )
 1176  
             {
 1177  0
                 if ( Developer.class.isAssignableFrom( arg0.getClass() ) )
 1178  
                 {
 1179  0
                     Developer developer0 = (Developer) arg0;
 1180  0
                     Developer developer1 = (Developer) arg1;
 1181  
 
 1182  0
                     if ( developer0.getName() == null )
 1183  
                     {
 1184  0
                         return -1;
 1185  
                     }
 1186  0
                     if ( developer1.getName() == null )
 1187  
                     {
 1188  0
                         return +1;
 1189  
                     }
 1190  
 
 1191  0
                     return developer0.getName().compareTo( developer1.getName() );
 1192  
                 }
 1193  
 
 1194  0
                 Contributor contributor0 = (Contributor) arg0;
 1195  0
                 Contributor contributor1 = (Contributor) arg1;
 1196  
 
 1197  0
                 if ( contributor0.getName() == null )
 1198  
                 {
 1199  0
                     return -1;
 1200  
                 }
 1201  0
                 if ( contributor1.getName() == null )
 1202  
                 {
 1203  0
                     return +1;
 1204  
                 }
 1205  
 
 1206  0
                 return contributor0.getName().compareTo( contributor1.getName() );
 1207  
             }
 1208  
         } );
 1209  
 
 1210  0
         for ( Iterator it = developersOrContributors.iterator(); it.hasNext(); )
 1211  
         {
 1212  0
             Object obj = it.next();
 1213  0
             writeDeveloperOrContributor( writer, obj, doapType );
 1214  0
         }
 1215  0
     }
 1216  
 
 1217  
     /**
 1218  
      * Writer a single developer or contributor
 1219  
      *
 1220  
      * @param writer not null
 1221  
      * @param developerOrContributor not null, instance of <code>{@link Developer}/{@link Contributor}</code>
 1222  
      * @param doapType not null
 1223  
      * @see <a href="http://usefulinc.com/ns/doap#maintainer">http://usefulinc.com/ns/doap#maintainer</a>
 1224  
      * @see <a href="http://usefulinc.com/ns/doap#developer">http://usefulinc.com/ns/doap#developer</a>
 1225  
      * @see <a href="http://usefulinc.com/ns/doap#documenter">http://usefulinc.com/ns/doap#documenter</a>
 1226  
      * @see <a href="http://usefulinc.com/ns/doap#translator">http://usefulinc.com/ns/doap#translator</a>
 1227  
      * @see <a href="http://usefulinc.com/ns/doap#tester">http://usefulinc.com/ns/doap#tester</a>
 1228  
      * @see <a href="http://usefulinc.com/ns/doap#helper">http://usefulinc.com/ns/doap#helper</a>
 1229  
      * @see <a href="http://xmlns.com/foaf/0.1/Person">http://xmlns.com/foaf/0.1/Person</a>
 1230  
      * @see <a href="http://xmlns.com/foaf/0.1/name">http://xmlns.com/foaf/0.1/name</a>
 1231  
      * @see <a href="http://xmlns.com/foaf/0.1/mbox">http://xmlns.com/foaf/0.1/mbox</a>
 1232  
      * @see <a href="http://xmlns.com/foaf/0.1/Organization">http://xmlns.com/foaf/0.1/Organization</a>
 1233  
      * @see <a href="http://xmlns.com/foaf/0.1/homepage">http://xmlns.com/foaf/0.1/homepage</a>
 1234  
      */
 1235  
     private void writeDeveloperOrContributor( XMLWriter writer, Object developerOrContributor, String doapType )
 1236  
     {
 1237  0
         if ( developerOrContributor == null )
 1238  
         {
 1239  0
             return;
 1240  
         }
 1241  
 
 1242  0
         if ( StringUtils.isEmpty( doapType ) )
 1243  
         {
 1244  0
             throw new IllegalArgumentException( "doapType is required." );
 1245  
         }
 1246  
 
 1247  
         String name;
 1248  
         String email;
 1249  
         String organization;
 1250  
         String homepage;
 1251  
 
 1252  0
         if ( Developer.class.isAssignableFrom( developerOrContributor.getClass() ) )
 1253  
         {
 1254  0
             Developer d = (Developer) developerOrContributor;
 1255  0
             name = d.getName();
 1256  0
             email = d.getEmail();
 1257  0
             organization = d.getOrganization();
 1258  0
             homepage = d.getUrl();
 1259  0
         }
 1260  
         else
 1261  
         {
 1262  0
             Contributor c = (Contributor) developerOrContributor;
 1263  0
             name = c.getName();
 1264  0
             email = c.getEmail();
 1265  0
             organization = c.getOrganization();
 1266  0
             homepage = c.getUrl();
 1267  
         }
 1268  
 
 1269  
         // Name is required to write doap
 1270  0
         if ( StringUtils.isEmpty( name ) )
 1271  
         {
 1272  0
             return;
 1273  
         }
 1274  
 
 1275  0
         writer.startElement( doapType );
 1276  0
         writer.startElement( "foaf:Person" );
 1277  0
         writer.startElement( "foaf:name" );
 1278  0
         writer.writeText( name );
 1279  0
         writer.endElement(); // foaf:name
 1280  0
         if ( StringUtils.isNotEmpty( email ) )
 1281  
         {
 1282  0
             DoapUtil.writeRdfResourceElement( writer, "foaf:mbox", "mailto:" + email );
 1283  
         }
 1284  0
         if ( StringUtils.isNotEmpty( organization ) )
 1285  
         {
 1286  0
             DoapUtil.writeRdfResourceElement( writer, "foaf:Organization", organization );
 1287  
         }
 1288  0
         if ( StringUtils.isNotEmpty( homepage ) )
 1289  
         {
 1290  0
             DoapUtil.writeRdfResourceElement( writer, "foaf:homepage", homepage );
 1291  
         }
 1292  0
         writer.endElement(); // foaf:Person
 1293  0
         writer.endElement(); // doapType
 1294  0
     }
 1295  
 
 1296  
     /**
 1297  
      * Return a <code>SCM repository</code> defined by a given url
 1298  
      *
 1299  
      * @param scmUrl an SCM URL
 1300  
      * @return a valid SCM repository or null
 1301  
      */
 1302  
     private ScmRepository getScmRepository( String scmUrl )
 1303  
     {
 1304  0
         ScmRepository repo = null;
 1305  0
         if ( !StringUtils.isEmpty( scmUrl ) )
 1306  
         {
 1307  
             try
 1308  
             {
 1309  0
                 repo = scmManager.makeScmRepository( scmUrl );
 1310  
             }
 1311  0
             catch ( NoSuchScmProviderException e )
 1312  
             {
 1313  0
                 if ( getLog().isDebugEnabled() )
 1314  
                 {
 1315  0
                     getLog().debug( e.getMessage(), e );
 1316  
                 }
 1317  
             }
 1318  0
             catch ( ScmRepositoryException e )
 1319  
             {
 1320  0
                 if ( getLog().isDebugEnabled() )
 1321  
                 {
 1322  0
                     getLog().debug( e.getMessage(), e );
 1323  
                 }
 1324  0
             }
 1325  
         }
 1326  
 
 1327  0
         return repo;
 1328  
     }
 1329  
 
 1330  
     /**
 1331  
      * Write the ASF extensions
 1332  
      *
 1333  
      * @param writer not null
 1334  
      * @see <a href="http://svn.apache.org/repos/asf/infrastructure/site-tools/trunk/projects/asfext">
 1335  
      * http://svn.apache.org/repos/asf/infrastructure/site-tools/trunk/projects/asfext</a>
 1336  
      * @see <a href="http://projects.apache.org/docs/pmc.html">http://projects.apache.org/docs/pmc.html</a>
 1337  
      */
 1338  
     private void writeASFext( XMLWriter writer )
 1339  
     {
 1340  1
         XmlWriterUtil.writeLineBreak( writer );
 1341  1
         XmlWriterUtil.writeCommentText( writer, "ASF extension", 2 );
 1342  
 
 1343  
         // asfext:pmc
 1344  1
         if ( StringUtils.isNotEmpty( asfExtOptions.getPmc() ) )
 1345  
         {
 1346  0
             DoapUtil.writeRdfResourceElement( writer, "asfext:pmc", asfExtOptions.getPmc() );
 1347  
         }
 1348  
         else
 1349  
         {
 1350  1
             if ( StringUtils.isEmpty( project.getUrl() ) )
 1351  
             {
 1352  0
                 getLog().warn(
 1353  
                                "No project url discovered! According http://projects.apache.org/docs/pmc.html, "
 1354  
                                    + "asfext:pmc is required" );
 1355  
             }
 1356  
             else
 1357  
             {
 1358  1
                 DoapUtil.writeRdfResourceElement( writer, "asfext:pmc", project.getUrl() );
 1359  
             }
 1360  
         }
 1361  
 
 1362  
         // asfext:name
 1363  1
         if ( StringUtils.isNotEmpty( asfExtOptions.getName() ) )
 1364  
         {
 1365  0
             DoapUtil.writeRdfResourceElement( writer, "asfext:name", asfExtOptions.getName() );
 1366  
         }
 1367  
         else
 1368  
         {
 1369  1
             if ( StringUtils.isEmpty( project.getName() ) )
 1370  
             {
 1371  0
                 getLog().warn(
 1372  
                                "No project name discovered! According http://projects.apache.org/docs/pmc.html, "
 1373  
                                    + "asfext:name is required" );
 1374  
             }
 1375  
             else
 1376  
             {
 1377  
                 // Respect ASF rule
 1378  1
                 if ( !project.getName().trim().startsWith( "Apache" ) )
 1379  
                 {
 1380  1
                     DoapUtil.writeRdfResourceElement( writer, "asfext:name", "Apache " + project.getName().trim() );
 1381  
                 }
 1382  
                 else
 1383  
                 {
 1384  0
                     DoapUtil.writeRdfResourceElement( writer, "asfext:name", project.getName().trim() );
 1385  
                 }
 1386  
             }
 1387  
         }
 1388  
 
 1389  
         // asfext:charter
 1390  1
         if ( StringUtils.isEmpty( asfExtOptions.getCharter() ) )
 1391  
         {
 1392  1
             getLog().warn(
 1393  
                            "No charter specified! According http://projects.apache.org/docs/pmc.html, "
 1394  
                                + "charter is required" );
 1395  
         }
 1396  
         else
 1397  
         {
 1398  0
             DoapUtil.writeRdfResourceElement( writer, "asfext:charter", asfExtOptions.getCharter() );
 1399  
         }
 1400  
 
 1401  
         // asfext:chair
 1402  1
         List developers = project.getDevelopers();
 1403  
 
 1404  1
         if ( StringUtils.isNotEmpty( asfExtOptions.getChair() ) )
 1405  
         {
 1406  0
             writer.startElement( "asfext:chair" );
 1407  0
             writer.startElement( "foaf:Person" );
 1408  0
             writer.startElement( "foaf:name" );
 1409  0
             writer.writeText( asfExtOptions.getChair() );
 1410  0
             writer.endElement(); // foaf:name
 1411  0
             writer.endElement(); // foaf:Person
 1412  0
             writer.endElement(); // asfext:chair
 1413  
         }
 1414  
         else
 1415  
         {
 1416  1
             Developer chair = ASFExtOptions.findChair( developers );
 1417  1
             if ( chair != null )
 1418  
             {
 1419  0
                 writeDeveloperOrContributor( writer, chair, "asfext:chair" );
 1420  
             }
 1421  
             else
 1422  
             {
 1423  1
                 getLog().warn(
 1424  
                                "No chair man discovered! According http://projects.apache.org/docs/pmc.html, "
 1425  
                                    + "asfext:chair is required" );
 1426  
             }
 1427  
         }
 1428  
 
 1429  
         // asfext:member
 1430  1
         if ( developers != null && developers.size() > 0 )
 1431  
         {
 1432  0
             List pmcMember = ASFExtOptions.findPMCMembers( developers );
 1433  0
             for ( Iterator it = pmcMember.iterator(); it.hasNext(); )
 1434  
             {
 1435  0
                 Developer developer = (Developer) it.next();
 1436  
 
 1437  0
                 writeDeveloperOrContributor( writer, developer, "asfext:member" );
 1438  0
             }
 1439  
         }
 1440  
 
 1441  1
         writeASFImplements( writer );
 1442  1
     }
 1443  
 
 1444  
     /**
 1445  
      * Write the ASF implements.
 1446  
      *
 1447  
      * @param writer not null
 1448  
      * @see <a href="http://svn.apache.org/repos/asf/infrastructure/site-tools/trunk/projects/asfext">
 1449  
      * http://svn.apache.org/repos/asf/infrastructure/site-tools/trunk/projects/asfext</a>
 1450  
      * @see <a href="http://projects.apache.org/docs/standards.html">http://projects.apache.org/docs/standards.html</a>
 1451  
      */
 1452  
     private void writeASFImplements( XMLWriter writer )
 1453  
     {
 1454  1
         if ( asfExtOptions.getStandards() == null || asfExtOptions.getStandards().isEmpty() )
 1455  
         {
 1456  1
             return;
 1457  
         }
 1458  
 
 1459  0
         for ( Iterator it = asfExtOptions.getStandards().iterator(); it.hasNext(); )
 1460  
         {
 1461  0
             Standard standard = (Standard) it.next();
 1462  
 
 1463  0
             writer.startElement( "asfext:implements" );
 1464  0
             writer.startElement( "asfext:Standard" );
 1465  
 
 1466  0
             if ( StringUtils.isEmpty( standard.getTitle() ) )
 1467  
             {
 1468  0
                 getLog().warn(
 1469  
                                "No title specified! According http://projects.apache.org/docs/standards.html, "
 1470  
                                    + "asfext:title is required" );
 1471  
             }
 1472  
             else
 1473  
             {
 1474  0
                 writer.startElement( "asfext:title" );
 1475  0
                 writer.writeText( standard.getTitle() );
 1476  0
                 writer.endElement(); // asfext:title
 1477  
             }
 1478  
 
 1479  0
             if ( StringUtils.isEmpty( standard.getBody() ) )
 1480  
             {
 1481  0
                 getLog().warn(
 1482  
                                "No body specified! According http://projects.apache.org/docs/standards.html, "
 1483  
                                    + "asfext:body is required" );
 1484  
             }
 1485  
             else
 1486  
             {
 1487  0
                 writer.startElement( "asfext:body" );
 1488  0
                 writer.writeText( standard.getBody() );
 1489  0
                 writer.endElement(); // asfext:body
 1490  
             }
 1491  
 
 1492  0
             if ( StringUtils.isEmpty( standard.getId() ) )
 1493  
             {
 1494  0
                 getLog().warn(
 1495  
                                "No id specified! According http://projects.apache.org/docs/standards.html, "
 1496  
                                    + "asfext:id is required" );
 1497  
             }
 1498  
             else
 1499  
             {
 1500  0
                 writer.startElement( "asfext:id" );
 1501  0
                 writer.writeText( standard.getId() );
 1502  0
                 writer.endElement(); // asfext:id
 1503  
             }
 1504  
 
 1505  0
             if ( StringUtils.isNotEmpty( standard.getUrl() ) )
 1506  
             {
 1507  0
                 writer.startElement( "asfext:url" );
 1508  0
                 writer.writeText( standard.getUrl() );
 1509  0
                 writer.endElement(); // asfext:url
 1510  
             }
 1511  
 
 1512  0
             writer.endElement(); // asfext:Standard
 1513  0
             writer.endElement(); // asfext:implements
 1514  0
         }
 1515  0
     }
 1516  
 
 1517  
     // ----------------------------------------------------------------------
 1518  
     // Static methods
 1519  
     // ----------------------------------------------------------------------
 1520  
 
 1521  
     /**
 1522  
      * Compose a URL from two parts: a base URL and a file path. This method
 1523  
      * makes sure that there will not be two slash '/' characters after each
 1524  
      * other.
 1525  
      *
 1526  
      * @param base The base URL
 1527  
      * @param path The file
 1528  
      * @return the url with base and path
 1529  
      */
 1530  
     private static String composeUrl( String base, String path )
 1531  
     {
 1532  2
         if ( base.endsWith( "/" ) && path.startsWith( "/" ) )
 1533  
         {
 1534  0
             return base + path.substring( 1 );
 1535  
         }
 1536  
 
 1537  2
         return base + path;
 1538  
     }
 1539  
 
 1540  
     /**
 1541  
      * Convenience method that return true is the defined <code>SCM repository</code> is a known provider.
 1542  
      * <p>
 1543  
      * Actually, we fully support Clearcase, CVS, Perforce, Starteam, SVN by the maven-scm-providers component.
 1544  
      * </p>
 1545  
      *
 1546  
      * @param scmRepository a SCM repository
 1547  
      * @param scmProvider a SCM provider name
 1548  
      * @return true if the provider of the given SCM repository is equal to the given scm provider.
 1549  
      * @see <a href="http://svn.apache.org/repos/asf/maven/scm/trunk/maven-scm-providers/">maven-scm-providers</a>
 1550  
      */
 1551  
     private static boolean isScmSystem( ScmRepository scmRepository, String scmProvider )
 1552  
     {
 1553  0
         if ( StringUtils.isEmpty( scmProvider ) )
 1554  
         {
 1555  0
             return false;
 1556  
         }
 1557  
 
 1558  0
         if ( scmRepository != null && scmProvider.equalsIgnoreCase( scmRepository.getProvider() ) )
 1559  
         {
 1560  0
             return true;
 1561  
         }
 1562  
 
 1563  0
         return false;
 1564  
     }
 1565  
 }