| package org.apache.maven.doxia.tools; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.Reader; |
| import java.io.StringReader; |
| import java.io.StringWriter; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.util.AbstractMap; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.StringTokenizer; |
| |
| import org.apache.commons.io.FilenameUtils; |
| import org.apache.maven.artifact.Artifact; |
| import org.apache.maven.artifact.factory.ArtifactFactory; |
| import org.apache.maven.artifact.repository.ArtifactRepository; |
| import org.apache.maven.artifact.resolver.ArtifactNotFoundException; |
| import org.apache.maven.artifact.resolver.ArtifactResolutionException; |
| import org.apache.maven.artifact.resolver.ArtifactResolver; |
| import org.apache.maven.artifact.versioning.DefaultArtifactVersion; |
| import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; |
| import org.apache.maven.artifact.versioning.VersionRange; |
| import org.apache.maven.doxia.site.decoration.Banner; |
| import org.apache.maven.doxia.site.decoration.DecorationModel; |
| import org.apache.maven.doxia.site.decoration.Menu; |
| import org.apache.maven.doxia.site.decoration.MenuItem; |
| import org.apache.maven.doxia.site.decoration.Skin; |
| import org.apache.maven.doxia.site.decoration.inheritance.DecorationModelInheritanceAssembler; |
| import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Reader; |
| import org.apache.maven.doxia.site.decoration.io.xpp3.DecorationXpp3Writer; |
| import org.apache.maven.model.DistributionManagement; |
| import org.apache.maven.model.Site; |
| import org.apache.maven.project.MavenProject; |
| import org.apache.maven.project.MavenProjectBuilder; |
| import org.apache.maven.project.ProjectBuildingException; |
| import org.apache.maven.reporting.MavenReport; |
| import org.codehaus.plexus.component.annotations.Component; |
| import org.codehaus.plexus.component.annotations.Requirement; |
| import org.codehaus.plexus.i18n.I18N; |
| import org.codehaus.plexus.logging.AbstractLogEnabled; |
| import org.codehaus.plexus.util.IOUtil; |
| import org.codehaus.plexus.util.ReaderFactory; |
| import org.codehaus.plexus.util.StringUtils; |
| import org.codehaus.plexus.interpolation.EnvarBasedValueSource; |
| import org.codehaus.plexus.interpolation.InterpolationException; |
| import org.codehaus.plexus.interpolation.MapBasedValueSource; |
| import org.codehaus.plexus.interpolation.ObjectBasedValueSource; |
| import org.codehaus.plexus.interpolation.PrefixedObjectValueSource; |
| import org.codehaus.plexus.interpolation.PrefixedPropertiesValueSource; |
| import org.codehaus.plexus.interpolation.RegexBasedInterpolator; |
| import org.codehaus.plexus.util.xml.pull.XmlPullParserException; |
| |
| /** |
| * Default implementation of the site tool. |
| * |
| * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a> |
| */ |
| @Component( role = SiteTool.class ) |
| public class DefaultSiteTool |
| extends AbstractLogEnabled |
| implements SiteTool |
| { |
| // ---------------------------------------------------------------------- |
| // Components |
| // ---------------------------------------------------------------------- |
| |
| /** |
| * The component that is used to resolve additional artifacts required. |
| */ |
| @Requirement |
| private ArtifactResolver artifactResolver; |
| |
| /** |
| * The component used for creating artifact instances. |
| */ |
| @Requirement |
| private ArtifactFactory artifactFactory; |
| |
| /** |
| * Internationalization. |
| */ |
| @Requirement |
| protected I18N i18n; |
| |
| /** |
| * The component for assembling inheritance. |
| */ |
| @Requirement |
| protected DecorationModelInheritanceAssembler assembler; |
| |
| /** |
| * Project builder (deprecated in Maven 3: should use ProjectBuilder, which will avoid |
| * issues like DOXIASITETOOLS-166) |
| */ |
| @Requirement |
| protected MavenProjectBuilder mavenProjectBuilder; |
| |
| // ---------------------------------------------------------------------- |
| // Public methods |
| // ---------------------------------------------------------------------- |
| |
| /** {@inheritDoc} */ |
| public Artifact getSkinArtifactFromRepository( ArtifactRepository localRepository, |
| List<ArtifactRepository> remoteArtifactRepositories, |
| DecorationModel decoration ) |
| throws SiteToolException |
| { |
| checkNotNull( "localRepository", localRepository ); |
| checkNotNull( "remoteArtifactRepositories", remoteArtifactRepositories ); |
| checkNotNull( "decoration", decoration ); |
| |
| Skin skin = decoration.getSkin(); |
| |
| if ( skin == null ) |
| { |
| skin = Skin.getDefaultSkin(); |
| } |
| |
| String version = skin.getVersion(); |
| Artifact artifact; |
| try |
| { |
| if ( version == null ) |
| { |
| version = Artifact.RELEASE_VERSION; |
| } |
| VersionRange versionSpec = VersionRange.createFromVersionSpec( version ); |
| artifact = artifactFactory.createDependencyArtifact( skin.getGroupId(), skin.getArtifactId(), versionSpec, |
| "jar", null, null ); |
| |
| artifactResolver.resolve( artifact, remoteArtifactRepositories, localRepository ); |
| } |
| catch ( InvalidVersionSpecificationException e ) |
| { |
| throw new SiteToolException( "InvalidVersionSpecificationException: The skin version '" + version |
| + "' is not valid: " + e.getMessage(), e ); |
| } |
| catch ( ArtifactResolutionException e ) |
| { |
| throw new SiteToolException( "ArtifactResolutionException: Unable to find skin", e ); |
| } |
| catch ( ArtifactNotFoundException e ) |
| { |
| throw new SiteToolException( "ArtifactNotFoundException: The skin does not exist: " + e.getMessage(), e ); |
| } |
| |
| return artifact; |
| } |
| |
| /** {@inheritDoc} */ |
| public Artifact getDefaultSkinArtifact( ArtifactRepository localRepository, |
| List<ArtifactRepository> remoteArtifactRepositories ) |
| throws SiteToolException |
| { |
| return getSkinArtifactFromRepository( localRepository, remoteArtifactRepositories, new DecorationModel() ); |
| } |
| |
| /** {@inheritDoc} */ |
| public String getRelativePath( String to, String from ) |
| { |
| checkNotNull( "to", to ); |
| checkNotNull( "from", from ); |
| |
| URL toUrl = null; |
| URL fromUrl = null; |
| |
| String toPath = to; |
| String fromPath = from; |
| |
| try |
| { |
| toUrl = new URL( to ); |
| } |
| catch ( MalformedURLException e ) |
| { |
| try |
| { |
| toUrl = new File( getNormalizedPath( to ) ).toURI().toURL(); |
| } |
| catch ( MalformedURLException e1 ) |
| { |
| getLogger().warn( "Unable to load a URL for '" + to + "': " + e.getMessage() ); |
| } |
| } |
| |
| try |
| { |
| fromUrl = new URL( from ); |
| } |
| catch ( MalformedURLException e ) |
| { |
| try |
| { |
| fromUrl = new File( getNormalizedPath( from ) ).toURI().toURL(); |
| } |
| catch ( MalformedURLException e1 ) |
| { |
| getLogger().warn( "Unable to load a URL for '" + from + "': " + e.getMessage() ); |
| } |
| } |
| |
| if ( toUrl != null && fromUrl != null ) |
| { |
| // URLs, determine if they share protocol and domain info |
| |
| if ( ( toUrl.getProtocol().equalsIgnoreCase( fromUrl.getProtocol() ) ) |
| && ( toUrl.getHost().equalsIgnoreCase( fromUrl.getHost() ) ) |
| && ( toUrl.getPort() == fromUrl.getPort() ) ) |
| { |
| // shared URL domain details, use URI to determine relative path |
| |
| toPath = toUrl.getFile(); |
| fromPath = fromUrl.getFile(); |
| } |
| else |
| { |
| // don't share basic URL information, no relative available |
| |
| return to; |
| } |
| } |
| else if ( ( toUrl != null && fromUrl == null ) || ( toUrl == null && fromUrl != null ) ) |
| { |
| // one is a URL and the other isn't, no relative available. |
| |
| return to; |
| } |
| |
| // either the two locations are not URLs or if they are they |
| // share the common protocol and domain info and we are left |
| // with their URI information |
| |
| String relativePath = getRelativeFilePath( fromPath, toPath ); |
| |
| if ( relativePath == null ) |
| { |
| relativePath = to; |
| } |
| |
| if ( getLogger().isDebugEnabled() && !relativePath.toString().equals( to ) ) |
| { |
| getLogger().debug( "Mapped url: " + to + " to relative path: " + relativePath ); |
| } |
| |
| return relativePath; |
| } |
| |
| private static String getRelativeFilePath( final String oldPath, final String newPath ) |
| { |
| // normalize the path delimiters |
| |
| String fromPath = new File( oldPath ).getPath(); |
| String toPath = new File( newPath ).getPath(); |
| |
| // strip any leading slashes if its a windows path |
| if ( toPath.matches( "^\\[a-zA-Z]:" ) ) |
| { |
| toPath = toPath.substring( 1 ); |
| } |
| if ( fromPath.matches( "^\\[a-zA-Z]:" ) ) |
| { |
| fromPath = fromPath.substring( 1 ); |
| } |
| |
| // lowercase windows drive letters. |
| if ( fromPath.startsWith( ":", 1 ) ) |
| { |
| fromPath = Character.toLowerCase( fromPath.charAt( 0 ) ) + fromPath.substring( 1 ); |
| } |
| if ( toPath.startsWith( ":", 1 ) ) |
| { |
| toPath = Character.toLowerCase( toPath.charAt( 0 ) ) + toPath.substring( 1 ); |
| } |
| |
| // check for the presence of windows drives. No relative way of |
| // traversing from one to the other. |
| |
| if ( ( toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) |
| && ( !toPath.substring( 0, 1 ).equals( fromPath.substring( 0, 1 ) ) ) ) |
| { |
| // they both have drive path element but they don't match, no |
| // relative path |
| |
| return null; |
| } |
| |
| if ( ( toPath.startsWith( ":", 1 ) && !fromPath.startsWith( ":", 1 ) ) |
| || ( !toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) ) |
| { |
| |
| // one has a drive path element and the other doesn't, no relative |
| // path. |
| |
| return null; |
| |
| } |
| |
| final String relativePath = buildRelativePath( toPath, fromPath, File.separatorChar ); |
| |
| return relativePath.toString(); |
| } |
| |
| /** {@inheritDoc} */ |
| public File getSiteDescriptor( File siteDirectory, Locale locale ) |
| { |
| checkNotNull( "siteDirectory", siteDirectory ); |
| final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; |
| |
| File siteDescriptor = new File( siteDirectory, "site_" + llocale.getLanguage() + ".xml" ); |
| |
| if ( !siteDescriptor.isFile() ) |
| { |
| siteDescriptor = new File( siteDirectory, "site.xml" ); |
| } |
| return siteDescriptor; |
| } |
| |
| /** |
| * Get a site descriptor from one of the repositories. |
| * |
| * @param project the Maven project, not null. |
| * @param localRepository the Maven local repository, not null. |
| * @param repositories the Maven remote repositories, not null. |
| * @param locale the locale wanted for the site descriptor. If not null, searching for |
| * <code>site_<i>localeLanguage</i>.xml</code>, otherwise searching for <code>site.xml</code>. |
| * @return the site descriptor into the local repository after download of it from repositories or null if not |
| * found in repositories. |
| * @throws SiteToolException if any |
| */ |
| File getSiteDescriptorFromRepository( MavenProject project, ArtifactRepository localRepository, |
| List<ArtifactRepository> repositories, Locale locale ) |
| throws SiteToolException |
| { |
| checkNotNull( "project", project ); |
| checkNotNull( "localRepository", localRepository ); |
| checkNotNull( "repositories", repositories ); |
| |
| final Locale llocale = ( locale == null ) ? new Locale( "" ) : locale; |
| |
| try |
| { |
| return resolveSiteDescriptor( project, localRepository, repositories, llocale ); |
| } |
| catch ( ArtifactNotFoundException e ) |
| { |
| getLogger().debug( "ArtifactNotFoundException: Unable to locate site descriptor: " + e ); |
| return null; |
| } |
| catch ( ArtifactResolutionException e ) |
| { |
| throw new SiteToolException( "ArtifactResolutionException: Unable to locate site descriptor: " |
| + e.getMessage(), e ); |
| } |
| catch ( IOException e ) |
| { |
| throw new SiteToolException( "IOException: Unable to locate site descriptor: " + e.getMessage(), e ); |
| } |
| } |
| |
| /** |
| * Read site descriptor content from Reader, adding support for deprecated <code>${reports}</code>, |
| * <code>${parentProject}</code> and <code>${modules}</code> tags. |
| * |
| * @param reader |
| * @return the input content interpolated with deprecated tags |
| * @throws IOException |
| */ |
| private String readSiteDescriptor( Reader reader, String projectId ) |
| throws IOException |
| { |
| String siteDescriptorContent = IOUtil.toString( reader ); |
| |
| // This is to support the deprecated ${reports}, ${parentProject} and ${modules} tags. |
| Properties props = new Properties(); |
| props.put( "reports", "<menu ref=\"reports\"/>" ); |
| props.put( "modules", "<menu ref=\"modules\"/>" ); |
| props.put( "parentProject", "<menu ref=\"parent\"/>" ); |
| |
| // warn if interpolation required |
| for ( Object prop : props.keySet() ) |
| { |
| if ( siteDescriptorContent.contains( "$" + prop ) ) |
| { |
| getLogger().warn( "Site descriptor for " + projectId + " contains $" + prop |
| + ": should be replaced with " + props.getProperty( (String) prop ) ); |
| } |
| if ( siteDescriptorContent.contains( "${" + prop + "}" ) ) |
| { |
| getLogger().warn( "Site descriptor for " + projectId + " contains ${" + prop |
| + "}: should be replaced with " + props.getProperty( (String) prop ) ); |
| } |
| } |
| |
| return StringUtils.interpolate( siteDescriptorContent, props ); |
| } |
| |
| /** {@inheritDoc} */ |
| public DecorationModel getDecorationModel( File siteDirectory, Locale locale, MavenProject project, |
| List<MavenProject> reactorProjects, ArtifactRepository localRepository, |
| List<ArtifactRepository> repositories ) |
| throws SiteToolException |
| { |
| checkNotNull( "project", project ); |
| checkNotNull( "reactorProjects", reactorProjects ); |
| checkNotNull( "localRepository", localRepository ); |
| checkNotNull( "repositories", repositories ); |
| |
| final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; |
| |
| getLogger().debug( "Computing decoration model of " + project.getId() + " for locale " + llocale ); |
| |
| Map.Entry<DecorationModel, MavenProject> result = |
| getDecorationModel( 0, siteDirectory, llocale, project, reactorProjects, localRepository, repositories ); |
| DecorationModel decorationModel = result.getKey(); |
| MavenProject parentProject = result.getValue(); |
| |
| if ( decorationModel == null ) |
| { |
| getLogger().debug( "Using default site descriptor" ); |
| |
| String siteDescriptorContent; |
| |
| Reader reader = null; |
| try |
| { |
| // Note the default is not a super class - it is used when nothing else is found |
| reader = ReaderFactory.newXmlReader( getClass().getResourceAsStream( "/default-site.xml" ) ); |
| siteDescriptorContent = readSiteDescriptor( reader, "default-site.xml" ); |
| } |
| catch ( IOException e ) |
| { |
| throw new SiteToolException( "Error reading default site descriptor: " + e.getMessage(), e ); |
| } |
| finally |
| { |
| IOUtil.close( reader ); |
| } |
| |
| decorationModel = readDecorationModel( siteDescriptorContent ); |
| } |
| |
| // DecorationModel back to String to interpolate, then go back to DecorationModel |
| String siteDescriptorContent = decorationModelToString( decorationModel ); |
| |
| // "classical" late interpolation, after full inheritance |
| siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, false ); |
| |
| decorationModel = readDecorationModel( siteDescriptorContent ); |
| |
| if ( parentProject != null ) |
| { |
| populateParentMenu( decorationModel, llocale, project, parentProject, true ); |
| } |
| |
| try |
| { |
| populateModulesMenu( decorationModel, llocale, project, reactorProjects, localRepository, true ); |
| } |
| catch ( IOException e ) |
| { |
| throw new SiteToolException( "Error while populating modules menu: " + e.getMessage(), e ); |
| } |
| |
| if ( decorationModel.getBannerLeft() == null ) |
| { |
| // extra default to set |
| Banner banner = new Banner(); |
| banner.setName( project.getName() ); |
| decorationModel.setBannerLeft( banner ); |
| } |
| |
| return decorationModel; |
| } |
| |
| /** {@inheritDoc} */ |
| public String getInterpolatedSiteDescriptorContent( Map<String, String> props, MavenProject aProject, |
| String siteDescriptorContent ) |
| throws SiteToolException |
| { |
| checkNotNull( "props", props ); |
| |
| // "classical" late interpolation |
| return getInterpolatedSiteDescriptorContent( aProject, siteDescriptorContent, false ); |
| } |
| |
| private String getInterpolatedSiteDescriptorContent( MavenProject aProject, |
| String siteDescriptorContent, boolean isEarly ) |
| throws SiteToolException |
| { |
| checkNotNull( "aProject", aProject ); |
| checkNotNull( "siteDescriptorContent", siteDescriptorContent ); |
| |
| RegexBasedInterpolator interpolator = new RegexBasedInterpolator(); |
| |
| if ( isEarly ) |
| { |
| interpolator.addValueSource( new PrefixedObjectValueSource( "this.", aProject ) ); |
| interpolator.addValueSource( new PrefixedPropertiesValueSource( "this.", aProject.getProperties() ) ); |
| } |
| else |
| { |
| interpolator.addValueSource( new ObjectBasedValueSource( aProject ) ); |
| interpolator.addValueSource( new MapBasedValueSource( aProject.getProperties() ) ); |
| |
| try |
| { |
| interpolator.addValueSource( new EnvarBasedValueSource() ); |
| } |
| catch ( IOException e ) |
| { |
| // Prefer logging? |
| throw new SiteToolException( "IOException: cannot interpolate environment properties: " |
| + e.getMessage(), e ); |
| } |
| } |
| |
| try |
| { |
| // FIXME: this does not escape xml entities, see MSITE-226, PLXCOMP-118 |
| return interpolator.interpolate( siteDescriptorContent, isEarly ? null : "project" ); |
| } |
| catch ( InterpolationException e ) |
| { |
| throw new SiteToolException( "Cannot interpolate site descriptor: " + e.getMessage(), e ); |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| public MavenProject getParentProject( MavenProject aProject, List<MavenProject> reactorProjects, |
| ArtifactRepository localRepository ) |
| { |
| checkNotNull( "aProject", aProject ); |
| checkNotNull( "reactorProjects", reactorProjects ); |
| checkNotNull( "localRepository", localRepository ); |
| |
| if ( isMaven3OrMore() ) |
| { |
| // no need to make voodoo with Maven 3: job already done |
| return aProject.getParent(); |
| } |
| |
| MavenProject parentProject = null; |
| |
| MavenProject origParent = aProject.getParent(); |
| if ( origParent != null ) |
| { |
| for ( MavenProject reactorProject : reactorProjects ) |
| { |
| if ( reactorProject.getGroupId().equals( origParent.getGroupId() ) |
| && reactorProject.getArtifactId().equals( origParent.getArtifactId() ) |
| && reactorProject.getVersion().equals( origParent.getVersion() ) ) |
| { |
| parentProject = reactorProject; |
| |
| getLogger().debug( "Parent project " + origParent.getId() + " picked from reactor" ); |
| break; |
| } |
| } |
| |
| if ( parentProject == null && aProject.getBasedir() != null |
| && StringUtils.isNotEmpty( aProject.getModel().getParent().getRelativePath() ) ) |
| { |
| try |
| { |
| String relativePath = aProject.getModel().getParent().getRelativePath(); |
| |
| File pomFile = new File( aProject.getBasedir(), relativePath ); |
| |
| if ( pomFile.isDirectory() ) |
| { |
| pomFile = new File( pomFile, "pom.xml" ); |
| } |
| pomFile = new File( getNormalizedPath( pomFile.getPath() ) ); |
| |
| if ( pomFile.isFile() ) |
| { |
| MavenProject mavenProject = mavenProjectBuilder.build( pomFile, localRepository, null ); |
| |
| if ( mavenProject.getGroupId().equals( origParent.getGroupId() ) |
| && mavenProject.getArtifactId().equals( origParent.getArtifactId() ) |
| && mavenProject.getVersion().equals( origParent.getVersion() ) ) |
| { |
| parentProject = mavenProject; |
| |
| getLogger().debug( "Parent project " + origParent.getId() + " loaded from a relative path: " |
| + relativePath ); |
| } |
| } |
| } |
| catch ( ProjectBuildingException e ) |
| { |
| getLogger().info( "Unable to load parent project " + origParent.getId() + " from a relative path: " |
| + e.getMessage() ); |
| } |
| } |
| |
| if ( parentProject == null ) |
| { |
| try |
| { |
| parentProject = mavenProjectBuilder.buildFromRepository( aProject.getParentArtifact(), aProject |
| .getRemoteArtifactRepositories(), localRepository ); |
| |
| getLogger().debug( "Parent project " + origParent.getId() + " loaded from repository" ); |
| } |
| catch ( ProjectBuildingException e ) |
| { |
| getLogger().warn( "Unable to load parent project " + origParent.getId() + " from repository: " |
| + e.getMessage() ); |
| } |
| } |
| |
| if ( parentProject == null ) |
| { |
| // fallback to original parent, which may contain uninterpolated value (still need a unit test) |
| |
| parentProject = origParent; |
| |
| getLogger().debug( "Parent project " + origParent.getId() + " picked from original value" ); |
| } |
| } |
| return parentProject; |
| } |
| |
| /** |
| * Populate the pre-defined <code>parent</code> menu of the decoration model, |
| * if used through <code><menu ref="parent"/></code>. |
| * |
| * @param decorationModel the Doxia Sitetools DecorationModel, not null. |
| * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. |
| * @param project a Maven project, not null. |
| * @param parentProject a Maven parent project, not null. |
| * @param keepInheritedRefs used for inherited references. |
| */ |
| private void populateParentMenu( DecorationModel decorationModel, Locale locale, MavenProject project, |
| MavenProject parentProject, boolean keepInheritedRefs ) |
| { |
| checkNotNull( "decorationModel", decorationModel ); |
| checkNotNull( "project", project ); |
| checkNotNull( "parentProject", parentProject ); |
| |
| Menu menu = decorationModel.getMenuRef( "parent" ); |
| |
| if ( menu == null ) |
| { |
| return; |
| } |
| |
| if ( keepInheritedRefs && menu.isInheritAsRef() ) |
| { |
| return; |
| } |
| |
| final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; |
| |
| String parentUrl = getDistMgmntSiteUrl( parentProject ); |
| |
| if ( parentUrl != null ) |
| { |
| if ( parentUrl.endsWith( "/" ) ) |
| { |
| parentUrl += "index.html"; |
| } |
| else |
| { |
| parentUrl += "/index.html"; |
| } |
| |
| parentUrl = getRelativePath( parentUrl, getDistMgmntSiteUrl( project ) ); |
| } |
| else |
| { |
| // parent has no url, assume relative path is given by site structure |
| File parentBasedir = parentProject.getBasedir(); |
| // First make sure that the parent is available on the file system |
| if ( parentBasedir != null ) |
| { |
| // Try to find the relative path to the parent via the file system |
| String parentPath = parentBasedir.getAbsolutePath(); |
| String projectPath = project.getBasedir().getAbsolutePath(); |
| parentUrl = getRelativePath( parentPath, projectPath ) + "/index.html"; |
| } |
| } |
| |
| // Only add the parent menu if we were able to find a URL for it |
| if ( parentUrl == null ) |
| { |
| getLogger().warn( "Unable to find a URL to the parent project. The parent menu will NOT be added." ); |
| } |
| else |
| { |
| if ( menu.getName() == null ) |
| { |
| menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.parentproject" ) ); |
| } |
| |
| MenuItem item = new MenuItem(); |
| item.setName( parentProject.getName() ); |
| item.setHref( parentUrl ); |
| menu.addItem( item ); |
| } |
| } |
| |
| /** |
| * Populate the pre-defined <code>modules</code> menu of the decoration model, |
| * if used through <code><menu ref="modules"/></code>. |
| * |
| * @param decorationModel the Doxia Sitetools DecorationModel, not null. |
| * @param locale the locale used for the i18n in DecorationModel. If null, using the default locale in the jvm. |
| * @param project a Maven project, not null. |
| * @param reactorProjects the Maven reactor projects, not null. |
| * @param localRepository the Maven local repository, not null. |
| * @param keepInheritedRefs used for inherited references. |
| * @throws SiteToolException if any |
| * @throws IOException |
| */ |
| private void populateModulesMenu( DecorationModel decorationModel, Locale locale, MavenProject project, |
| List<MavenProject> reactorProjects, ArtifactRepository localRepository, |
| boolean keepInheritedRefs ) |
| throws SiteToolException, IOException |
| { |
| checkNotNull( "project", project ); |
| checkNotNull( "reactorProjects", reactorProjects ); |
| checkNotNull( "localRepository", localRepository ); |
| checkNotNull( "decorationModel", decorationModel ); |
| |
| Menu menu = decorationModel.getMenuRef( "modules" ); |
| |
| if ( menu == null ) |
| { |
| return; |
| } |
| |
| if ( keepInheritedRefs && menu.isInheritAsRef() ) |
| { |
| return; |
| } |
| |
| final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale ; |
| |
| // we require child modules and reactors to process module menu |
| if ( project.getModules().size() > 0 ) |
| { |
| if ( menu.getName() == null ) |
| { |
| menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectmodules" ) ); |
| } |
| |
| for ( String module : (List<String>) project.getModules() ) |
| { |
| MavenProject moduleProject = getModuleFromReactor( project, reactorProjects, module ); |
| |
| if ( moduleProject == null ) |
| { |
| getLogger().warn( "Module " + module |
| + " not found in reactor: loading locally" ); |
| |
| File f = new File( project.getBasedir(), module + "/pom.xml" ); |
| if ( f.exists() ) |
| { |
| try |
| { |
| moduleProject = mavenProjectBuilder.build( f, localRepository, null ); |
| } |
| catch ( ProjectBuildingException e ) |
| { |
| throw new SiteToolException( "Unable to read local module-POM", e ); |
| } |
| } |
| else |
| { |
| getLogger().warn( "No filesystem module-POM available" ); |
| |
| moduleProject = new MavenProject(); |
| moduleProject.setName( module ); |
| moduleProject.setDistributionManagement( new DistributionManagement() ); |
| moduleProject.getDistributionManagement().setSite( new Site() ); |
| moduleProject.getDistributionManagement().getSite().setUrl( module ); |
| } |
| } |
| |
| String siteUrl = getDistMgmntSiteUrl( moduleProject ); |
| String itemName = |
| ( moduleProject.getName() == null ) ? moduleProject.getArtifactId() : moduleProject.getName(); |
| |
| appendMenuItem( project, menu, itemName, siteUrl, moduleProject.getArtifactId() ); |
| } |
| } |
| else if ( decorationModel.getMenuRef( "modules" ).getInherit() == null ) |
| { |
| // only remove if project has no modules AND menu is not inherited, see MSHARED-174 |
| decorationModel.removeMenuRef( "modules" ); |
| } |
| } |
| |
| private static MavenProject getModuleFromReactor( MavenProject project, List<MavenProject> reactorProjects, |
| String module ) |
| throws IOException |
| { |
| File moduleBasedir = new File( project.getBasedir(), module ).getCanonicalFile(); |
| |
| for ( MavenProject reactorProject : reactorProjects ) |
| { |
| if ( moduleBasedir.equals( reactorProject.getBasedir() ) ) |
| { |
| return reactorProject; |
| } |
| } |
| |
| // module not found in reactor |
| return null; |
| } |
| |
| /** {@inheritDoc} */ |
| public void populateReportsMenu( DecorationModel decorationModel, Locale locale, |
| Map<String, List<MavenReport>> categories ) |
| { |
| checkNotNull( "decorationModel", decorationModel ); |
| checkNotNull( "categories", categories ); |
| |
| Menu menu = decorationModel.getMenuRef( "reports" ); |
| |
| if ( menu == null ) |
| { |
| return; |
| } |
| |
| final Locale llocale = ( locale == null ) ? Locale.getDefault() : locale; |
| |
| if ( menu.getName() == null ) |
| { |
| menu.setName( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectdocumentation" ) ); |
| } |
| |
| boolean found = false; |
| if ( menu.getItems().isEmpty() ) |
| { |
| List<MavenReport> categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_INFORMATION ); |
| if ( !isEmptyList( categoryReports ) ) |
| { |
| MenuItem item = createCategoryMenu( |
| i18n.getString( "site-tool", llocale, |
| "decorationModel.menu.projectinformation" ), |
| "/project-info.html", categoryReports, llocale ); |
| menu.getItems().add( item ); |
| found = true; |
| } |
| |
| categoryReports = categories.get( MavenReport.CATEGORY_PROJECT_REPORTS ); |
| if ( !isEmptyList( categoryReports ) ) |
| { |
| MenuItem item = |
| createCategoryMenu( i18n.getString( "site-tool", llocale, "decorationModel.menu.projectreports" ), |
| "/project-reports.html", categoryReports, llocale ); |
| menu.getItems().add( item ); |
| found = true; |
| } |
| } |
| if ( !found ) |
| { |
| decorationModel.removeMenuRef( "reports" ); |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| public List<Locale> getSiteLocales( String locales ) |
| { |
| if ( locales == null ) |
| { |
| return Collections.singletonList( DEFAULT_LOCALE ); |
| } |
| |
| String[] localesArray = StringUtils.split( locales, "," ); |
| List<Locale> localesList = new ArrayList<Locale>( localesArray.length ); |
| |
| for ( String localeString : localesArray ) |
| { |
| Locale locale = codeToLocale( localeString ); |
| |
| if ( locale == null ) |
| { |
| continue; |
| } |
| |
| if ( !Arrays.asList( Locale.getAvailableLocales() ).contains( locale ) ) |
| { |
| if ( getLogger().isWarnEnabled() ) |
| { |
| getLogger().warn( "The locale defined by '" + locale |
| + "' is not available in this Java Virtual Machine (" |
| + System.getProperty( "java.version" ) |
| + " from " + System.getProperty( "java.vendor" ) + ") - IGNORING" ); |
| } |
| continue; |
| } |
| |
| // Default bundles are in English |
| if ( ( !locale.getLanguage().equals( DEFAULT_LOCALE.getLanguage() ) ) |
| && ( !i18n.getBundle( "site-tool", locale ).getLocale().getLanguage() |
| .equals( locale.getLanguage() ) ) ) |
| { |
| if ( getLogger().isWarnEnabled() ) |
| { |
| getLogger().warn( "The locale '" + locale + "' (" + locale.getDisplayName( Locale.ENGLISH ) |
| + ") is not currently supported by Maven Site - IGNORING." |
| + "\nContributions are welcome and greatly appreciated!" |
| + "\nIf you want to contribute a new translation, please visit " |
| + "http://maven.apache.org/plugins/localization.html for detailed instructions." ); |
| } |
| |
| continue; |
| } |
| |
| localesList.add( locale ); |
| } |
| |
| if ( localesList.isEmpty() ) |
| { |
| localesList = Collections.singletonList( DEFAULT_LOCALE ); |
| } |
| |
| return localesList; |
| } |
| |
| /** |
| * Converts a locale code like "en", "en_US" or "en_US_win" to a <code>java.util.Locale</code> |
| * object. |
| * <p>If localeCode = <code>default</code>, return the current value of the default locale for this instance |
| * of the Java Virtual Machine.</p> |
| * |
| * @param localeCode the locale code string. |
| * @return a java.util.Locale object instanced or null if errors occurred |
| * @see <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Locale.html">java.util.Locale#getDefault()</a> |
| */ |
| private Locale codeToLocale( String localeCode ) |
| { |
| if ( localeCode == null ) |
| { |
| return null; |
| } |
| |
| if ( "default".equalsIgnoreCase( localeCode ) ) |
| { |
| return Locale.getDefault(); |
| } |
| |
| String language = ""; |
| String country = ""; |
| String variant = ""; |
| |
| StringTokenizer tokenizer = new StringTokenizer( localeCode, "_" ); |
| final int maxTokens = 3; |
| if ( tokenizer.countTokens() > maxTokens ) |
| { |
| if ( getLogger().isWarnEnabled() ) |
| { |
| getLogger().warn( "Invalid java.util.Locale format for '" + localeCode + "' entry - IGNORING" ); |
| } |
| return null; |
| } |
| |
| if ( tokenizer.hasMoreTokens() ) |
| { |
| language = tokenizer.nextToken(); |
| if ( tokenizer.hasMoreTokens() ) |
| { |
| country = tokenizer.nextToken(); |
| if ( tokenizer.hasMoreTokens() ) |
| { |
| variant = tokenizer.nextToken(); |
| } |
| } |
| } |
| |
| return new Locale( language, country, variant ); |
| } |
| |
| // ---------------------------------------------------------------------- |
| // Protected methods |
| // ---------------------------------------------------------------------- |
| |
| /** |
| * @param path could be null. |
| * @return the path normalized, i.e. by eliminating "/../" and "/./" in the path. |
| * @see FilenameUtils#normalize(String) |
| */ |
| protected static String getNormalizedPath( String path ) |
| { |
| String normalized = FilenameUtils.normalize( path ); |
| if ( normalized == null ) |
| { |
| normalized = path; |
| } |
| return ( normalized == null ) ? null : normalized.replace( '\\', '/' ); |
| } |
| |
| // ---------------------------------------------------------------------- |
| // Private methods |
| // ---------------------------------------------------------------------- |
| |
| /** |
| * @param project not null |
| * @param localRepository not null |
| * @param repositories not null |
| * @param locale not null |
| * @return the resolved site descriptor |
| * @throws IOException if any |
| * @throws ArtifactResolutionException if any |
| * @throws ArtifactNotFoundException if any |
| */ |
| private File resolveSiteDescriptor( MavenProject project, ArtifactRepository localRepository, |
| List<ArtifactRepository> repositories, Locale locale ) |
| throws IOException, ArtifactResolutionException, ArtifactNotFoundException |
| { |
| File result; |
| |
| // TODO: this is a bit crude - proper type, or proper handling as metadata rather than an artifact in 2.1? |
| Artifact artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), |
| project.getArtifactId(), |
| project.getVersion(), "xml", |
| "site_" + locale.getLanguage() ); |
| |
| boolean found = false; |
| try |
| { |
| artifactResolver.resolve( artifact, repositories, localRepository ); |
| |
| result = artifact.getFile(); |
| |
| // we use zero length files to avoid re-resolution (see below) |
| if ( result.length() > 0 ) |
| { |
| found = true; |
| } |
| else |
| { |
| getLogger().debug( "No site descriptor found for " + project.getId() + " for locale " |
| + locale.getLanguage() + ", trying without locale..." ); |
| } |
| } |
| catch ( ArtifactNotFoundException e ) |
| { |
| getLogger().debug( "Unable to locate site descriptor for locale " + locale.getLanguage() + ": " + e ); |
| |
| // we can afford to write an empty descriptor here as we don't expect it to turn up later in the remote |
| // repository, because the parent was already released (and snapshots are updated automatically if changed) |
| result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); |
| result.getParentFile().mkdirs(); |
| result.createNewFile(); |
| } |
| |
| if ( !found ) |
| { |
| artifact = artifactFactory.createArtifactWithClassifier( project.getGroupId(), project.getArtifactId(), |
| project.getVersion(), "xml", "site" ); |
| try |
| { |
| artifactResolver.resolve( artifact, repositories, localRepository ); |
| } |
| catch ( ArtifactNotFoundException e ) |
| { |
| // see above regarding this zero length file |
| result = new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ); |
| result.getParentFile().mkdirs(); |
| result.createNewFile(); |
| |
| throw e; |
| } |
| |
| result = artifact.getFile(); |
| |
| // we use zero length files to avoid re-resolution (see below) |
| if ( result.length() == 0 ) |
| { |
| getLogger().debug( "No site descriptor found for " + project.getId() + " without locale." ); |
| result = null; |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * @param depth depth of project |
| * @param siteDirectory, can be null if project.basedir is null, ie POM from repository |
| * @param locale not null |
| * @param project not null |
| * @param reactorProjects not null |
| * @param localRepository not null |
| * @param repositories not null |
| * @param origProps not null |
| * @return the decoration model depending the locale and the parent project |
| * @throws SiteToolException if any |
| */ |
| private Map.Entry<DecorationModel, MavenProject> getDecorationModel( int depth, File siteDirectory, Locale locale, |
| MavenProject project, |
| List<MavenProject> reactorProjects, |
| ArtifactRepository localRepository, |
| List<ArtifactRepository> repositories ) |
| throws SiteToolException |
| { |
| // 1. get site descriptor File |
| File siteDescriptor; |
| if ( project.getBasedir() == null ) |
| { |
| // POM is in the repository: look into the repository for site descriptor |
| try |
| { |
| siteDescriptor = getSiteDescriptorFromRepository( project, localRepository, repositories, locale ); |
| } |
| catch ( SiteToolException e ) |
| { |
| throw new SiteToolException( "The site descriptor cannot be resolved from the repository: " |
| + e.getMessage(), e ); |
| } |
| } |
| else |
| { |
| // POM is in build directory: look for site descriptor as local file |
| siteDescriptor = getSiteDescriptor( siteDirectory, locale ); |
| } |
| |
| // 2. read DecorationModel from site descriptor File and do early interpolation (${this.*}) |
| DecorationModel decoration = null; |
| Reader siteDescriptorReader = null; |
| try |
| { |
| if ( siteDescriptor != null && siteDescriptor.exists() ) |
| { |
| getLogger().debug( "Reading" + ( depth == 0 ? "" : ( " parent level " + depth ) ) |
| + " site descriptor from " + siteDescriptor ); |
| |
| siteDescriptorReader = ReaderFactory.newXmlReader( siteDescriptor ); |
| |
| String siteDescriptorContent = readSiteDescriptor( siteDescriptorReader, project.getId() ); |
| |
| // interpolate ${this.*} = early interpolation |
| siteDescriptorContent = getInterpolatedSiteDescriptorContent( project, siteDescriptorContent, true ); |
| |
| decoration = readDecorationModel( siteDescriptorContent ); |
| decoration.setLastModified( siteDescriptor.lastModified() ); |
| } |
| else |
| { |
| getLogger().debug( "No" + ( depth == 0 ? "" : ( " parent level " + depth ) ) + " site descriptor." ); |
| } |
| } |
| catch ( IOException e ) |
| { |
| throw new SiteToolException( "The site descriptor for " + project.getId() + " cannot be read from " |
| + siteDescriptor, e ); |
| } |
| finally |
| { |
| IOUtil.close( siteDescriptorReader ); |
| } |
| |
| // 3. look for parent project |
| MavenProject parentProject = getParentProject( project, reactorProjects, localRepository ); |
| |
| // 4. merge with parent project DecorationModel |
| if ( parentProject != null && ( decoration == null || decoration.isMergeParent() ) ) |
| { |
| depth++; |
| getLogger().debug( "Looking for site descriptor of level " + depth + " parent project: " |
| + parentProject.getId() ); |
| |
| File parentSiteDirectory = null; |
| if ( parentProject.getBasedir() != null ) |
| { |
| // extrapolate parent project site directory |
| String siteRelativePath = getRelativeFilePath( project.getBasedir().getAbsolutePath(), |
| siteDescriptor.getParentFile().getAbsolutePath() ); |
| |
| parentSiteDirectory = new File( parentProject.getBasedir(), siteRelativePath ); |
| // notice: using same siteRelativePath for parent as current project; may be wrong if site plugin |
| // has different configuration. But this is a rare case (this only has impact if parent is from reactor) |
| } |
| |
| DecorationModel parentDecoration = |
| getDecorationModel( depth, parentSiteDirectory, locale, parentProject, reactorProjects, localRepository, |
| repositories ).getKey(); |
| |
| // MSHARED-116 requires an empty decoration model (instead of a null one) |
| // MSHARED-145 requires us to do this only if there is a parent to merge it with |
| if ( decoration == null && parentDecoration != null ) |
| { |
| // we have no site descriptor: merge the parent into an empty one |
| decoration = new DecorationModel(); |
| } |
| |
| String name = project.getName(); |
| if ( decoration != null && StringUtils.isNotEmpty( decoration.getName() ) ) |
| { |
| name = decoration.getName(); |
| } |
| |
| // Merge the parent and child DecorationModels |
| String projectDistMgmnt = getDistMgmntSiteUrl( project ); |
| String parentDistMgmnt = getDistMgmntSiteUrl( parentProject ); |
| if ( getLogger().isDebugEnabled() ) |
| { |
| getLogger().debug( "Site decoration model inheritance: assembling child with level " + depth |
| + " parent: distributionManagement.site.url child = " + projectDistMgmnt + " and parent = " |
| + parentDistMgmnt ); |
| } |
| assembler.assembleModelInheritance( name, decoration, parentDecoration, projectDistMgmnt, |
| parentDistMgmnt == null ? projectDistMgmnt : parentDistMgmnt ); |
| } |
| |
| return new AbstractMap.SimpleEntry<DecorationModel, MavenProject>( decoration, parentProject ); |
| } |
| |
| /** |
| * @param siteDescriptorContent not null |
| * @return the decoration model object |
| * @throws SiteToolException if any |
| */ |
| private DecorationModel readDecorationModel( String siteDescriptorContent ) |
| throws SiteToolException |
| { |
| try |
| { |
| return new DecorationXpp3Reader().read( new StringReader( siteDescriptorContent ) ); |
| } |
| catch ( XmlPullParserException e ) |
| { |
| throw new SiteToolException( "Error parsing site descriptor", e ); |
| } |
| catch ( IOException e ) |
| { |
| throw new SiteToolException( "Error reading site descriptor", e ); |
| } |
| } |
| |
| private String decorationModelToString( DecorationModel decoration ) |
| throws SiteToolException |
| { |
| StringWriter writer = new StringWriter(); |
| |
| try |
| { |
| new DecorationXpp3Writer().write( writer, decoration ); |
| return writer.toString(); |
| } |
| catch ( IOException e ) |
| { |
| throw new SiteToolException( "Error reading site descriptor", e ); |
| } |
| finally |
| { |
| IOUtil.close( writer ); |
| } |
| } |
| |
| private static String buildRelativePath( final String toPath, final String fromPath, final char separatorChar ) |
| { |
| // use tokenizer to traverse paths and for lazy checking |
| StringTokenizer toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); |
| StringTokenizer fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); |
| |
| int count = 0; |
| |
| // walk along the to path looking for divergence from the from path |
| while ( toTokeniser.hasMoreTokens() && fromTokeniser.hasMoreTokens() ) |
| { |
| if ( separatorChar == '\\' ) |
| { |
| if ( !fromTokeniser.nextToken().equalsIgnoreCase( toTokeniser.nextToken() ) ) |
| { |
| break; |
| } |
| } |
| else |
| { |
| if ( !fromTokeniser.nextToken().equals( toTokeniser.nextToken() ) ) |
| { |
| break; |
| } |
| } |
| |
| count++; |
| } |
| |
| // reinitialize the tokenizers to count positions to retrieve the |
| // gobbled token |
| |
| toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); |
| fromTokeniser = new StringTokenizer( fromPath, String.valueOf( separatorChar ) ); |
| |
| while ( count-- > 0 ) |
| { |
| fromTokeniser.nextToken(); |
| toTokeniser.nextToken(); |
| } |
| |
| StringBuilder relativePath = new StringBuilder(); |
| |
| // add back refs for the rest of from location. |
| while ( fromTokeniser.hasMoreTokens() ) |
| { |
| fromTokeniser.nextToken(); |
| |
| relativePath.append( ".." ); |
| |
| if ( fromTokeniser.hasMoreTokens() ) |
| { |
| relativePath.append( separatorChar ); |
| } |
| } |
| |
| if ( relativePath.length() != 0 && toTokeniser.hasMoreTokens() ) |
| { |
| relativePath.append( separatorChar ); |
| } |
| |
| // add fwd fills for whatever's left of to. |
| while ( toTokeniser.hasMoreTokens() ) |
| { |
| relativePath.append( toTokeniser.nextToken() ); |
| |
| if ( toTokeniser.hasMoreTokens() ) |
| { |
| relativePath.append( separatorChar ); |
| } |
| } |
| return relativePath.toString(); |
| } |
| |
| /** |
| * @param project not null |
| * @param menu not null |
| * @param name not null |
| * @param href could be null |
| * @param defaultHref not null |
| */ |
| private void appendMenuItem( MavenProject project, Menu menu, String name, String href, String defaultHref ) |
| { |
| String selectedHref = href; |
| |
| if ( selectedHref == null ) |
| { |
| selectedHref = defaultHref; |
| } |
| |
| MenuItem item = new MenuItem(); |
| item.setName( name ); |
| |
| String baseUrl = getDistMgmntSiteUrl( project ); |
| if ( baseUrl != null ) |
| { |
| selectedHref = getRelativePath( selectedHref, baseUrl ); |
| } |
| |
| if ( selectedHref.endsWith( "/" ) ) |
| { |
| item.setHref( selectedHref + "index.html" ); |
| } |
| else |
| { |
| item.setHref( selectedHref + "/index.html" ); |
| } |
| menu.addItem( item ); |
| } |
| |
| /** |
| * @param name not null |
| * @param href not null |
| * @param categoryReports not null |
| * @param locale not null |
| * @return the menu item object |
| */ |
| private MenuItem createCategoryMenu( String name, String href, List<MavenReport> categoryReports, Locale locale ) |
| { |
| MenuItem item = new MenuItem(); |
| item.setName( name ); |
| item.setCollapse( true ); |
| item.setHref( href ); |
| |
| // MSHARED-172, allow reports to define their order in some other way? |
| //Collections.sort( categoryReports, new ReportComparator( locale ) ); |
| |
| for ( MavenReport report : categoryReports ) |
| { |
| MenuItem subitem = new MenuItem(); |
| subitem.setName( report.getName( locale ) ); |
| subitem.setHref( report.getOutputName() + ".html" ); |
| item.getItems().add( subitem ); |
| } |
| |
| return item; |
| } |
| |
| // ---------------------------------------------------------------------- |
| // static methods |
| // ---------------------------------------------------------------------- |
| |
| /** |
| * Convenience method. |
| * |
| * @param list could be null |
| * @return true if the list is <code>null</code> or empty |
| */ |
| private static boolean isEmptyList( List<?> list ) |
| { |
| return list == null || list.isEmpty(); |
| } |
| |
| /** |
| * Return distributionManagement.site.url if defined, null otherwise. |
| * |
| * @param project not null |
| * @return could be null |
| */ |
| private static String getDistMgmntSiteUrl( MavenProject project ) |
| { |
| return getDistMgmntSiteUrl( project.getDistributionManagement() ); |
| } |
| |
| private static String getDistMgmntSiteUrl( DistributionManagement distMgmnt ) |
| { |
| if ( distMgmnt != null && distMgmnt.getSite() != null && distMgmnt.getSite().getUrl() != null ) |
| { |
| return urlEncode( distMgmnt.getSite().getUrl() ); |
| } |
| |
| return null; |
| } |
| |
| private static String urlEncode( final String url ) |
| { |
| if ( url == null ) |
| { |
| return null; |
| } |
| |
| try |
| { |
| return new File( url ).toURI().toURL().toExternalForm(); |
| } |
| catch ( MalformedURLException ex ) |
| { |
| return url; // this will then throw somewhere else |
| } |
| } |
| |
| private void checkNotNull( String name, Object value ) |
| { |
| if ( value == null ) |
| { |
| throw new IllegalArgumentException( "The parameter '" + name + "' cannot be null." ); |
| } |
| } |
| |
| /** |
| * Check the current Maven version to see if it's Maven 3.0 or newer. |
| */ |
| private static boolean isMaven3OrMore() |
| { |
| return new DefaultArtifactVersion( getMavenVersion() ).getMajorVersion() >= 3; |
| } |
| |
| private static String getMavenVersion() |
| { |
| // This relies on the fact that MavenProject is the in core classloader |
| // and that the core classloader is for the maven-core artifact |
| // and that should have a pom.properties file |
| // if this ever changes, we will have to revisit this code. |
| final Properties properties = new Properties(); |
| final String corePomProperties = "META-INF/maven/org.apache.maven/maven-core/pom.properties"; |
| final InputStream in = MavenProject.class.getClassLoader().getResourceAsStream( corePomProperties ); |
| try |
| { |
| properties.load( in ); |
| } |
| catch ( IOException ioe ) |
| { |
| return ""; |
| } |
| finally |
| { |
| IOUtil.close( in ); |
| } |
| |
| return properties.getProperty( "version" ).trim(); |
| } |
| } |