Coverage Report - org.apache.maven.shared.release.phase.AbstractRewritePomsPhase
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractRewritePomsPhase
91%
281/308
76%
111/146
5,217
 
 1  
 package org.apache.maven.shared.release.phase;
 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.StringReader;
 25  
 import java.io.StringWriter;
 26  
 import java.io.Writer;
 27  
 import java.util.ArrayList;
 28  
 import java.util.Arrays;
 29  
 import java.util.Collection;
 30  
 import java.util.Collections;
 31  
 import java.util.Iterator;
 32  
 import java.util.List;
 33  
 import java.util.Map;
 34  
 import java.util.regex.Matcher;
 35  
 import java.util.regex.Pattern;
 36  
 
 37  
 import org.apache.maven.artifact.Artifact;
 38  
 import org.apache.maven.artifact.ArtifactUtils;
 39  
 import org.apache.maven.model.Model;
 40  
 import org.apache.maven.model.Scm;
 41  
 import org.apache.maven.project.MavenProject;
 42  
 import org.apache.maven.scm.ScmException;
 43  
 import org.apache.maven.scm.ScmFileSet;
 44  
 import org.apache.maven.scm.command.edit.EditScmResult;
 45  
 import org.apache.maven.scm.manager.NoSuchScmProviderException;
 46  
 import org.apache.maven.scm.provider.ScmProvider;
 47  
 import org.apache.maven.scm.repository.ScmRepository;
 48  
 import org.apache.maven.scm.repository.ScmRepositoryException;
 49  
 import org.apache.maven.shared.release.ReleaseExecutionException;
 50  
 import org.apache.maven.shared.release.ReleaseFailureException;
 51  
 import org.apache.maven.shared.release.ReleaseResult;
 52  
 import org.apache.maven.shared.release.config.ReleaseDescriptor;
 53  
 import org.apache.maven.shared.release.env.ReleaseEnvironment;
 54  
 import org.apache.maven.shared.release.scm.IdentifiedScm;
 55  
 import org.apache.maven.shared.release.scm.ReleaseScmCommandException;
 56  
 import org.apache.maven.shared.release.scm.ReleaseScmRepositoryException;
 57  
 import org.apache.maven.shared.release.scm.ScmRepositoryConfigurator;
 58  
 import org.apache.maven.shared.release.util.ReleaseUtil;
 59  
 import org.codehaus.plexus.interpolation.InterpolationException;
 60  
 import org.codehaus.plexus.interpolation.MapBasedValueSource;
 61  
 import org.codehaus.plexus.interpolation.ObjectBasedValueSource;
 62  
 import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor;
 63  
 import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
 64  
 import org.codehaus.plexus.interpolation.StringSearchInterpolator;
 65  
 import org.codehaus.plexus.util.IOUtil;
 66  
 import org.codehaus.plexus.util.WriterFactory;
 67  
 import org.jdom.CDATA;
 68  
 import org.jdom.Comment;
 69  
 import org.jdom.Document;
 70  
 import org.jdom.Element;
 71  
 import org.jdom.JDOMException;
 72  
 import org.jdom.Namespace;
 73  
 import org.jdom.Text;
 74  
 import org.jdom.filter.ContentFilter;
 75  
 import org.jdom.filter.ElementFilter;
 76  
 import org.jdom.input.SAXBuilder;
 77  
 import org.jdom.output.Format;
 78  
 import org.jdom.output.XMLOutputter;
 79  
 
 80  
 /**
 81  
  * Base class for rewriting phases.
 82  
  *
 83  
  * @author <a href="mailto:brett@apache.org">Brett Porter</a>
 84  
  */
 85  478
 public abstract class AbstractRewritePomsPhase
 86  
     extends AbstractReleasePhase
 87  
 {
 88  
     /**
 89  
      * Tool that gets a configured SCM repository from release configuration.
 90  
      */
 91  
     private ScmRepositoryConfigurator scmRepositoryConfigurator;
 92  
 
 93  
     /**
 94  
      * Configuration item for the suffix to add to rewritten POMs when simulating.
 95  
      */
 96  
     private String pomSuffix;
 97  
 
 98  478
     private String ls = ReleaseUtil.LS;
 99  
 
 100  
     public void setLs( String ls )
 101  
     {
 102  2
         this.ls = ls;
 103  2
     }
 104  
 
 105  
     public ReleaseResult execute( ReleaseDescriptor releaseDescriptor, ReleaseEnvironment releaseEnvironment,
 106  
                                   List<MavenProject> reactorProjects )
 107  
         throws ReleaseExecutionException, ReleaseFailureException
 108  
     {
 109  224
         ReleaseResult result = new ReleaseResult();
 110  
 
 111  224
         transform( releaseDescriptor, releaseEnvironment, reactorProjects, false, result );
 112  
 
 113  166
         result.setResultCode( ReleaseResult.SUCCESS );
 114  
 
 115  166
         return result;
 116  
     }
 117  
 
 118  
     private void transform( ReleaseDescriptor releaseDescriptor, ReleaseEnvironment releaseEnvironment,
 119  
                             List<MavenProject> reactorProjects, boolean simulate, ReleaseResult result )
 120  
         throws ReleaseExecutionException, ReleaseFailureException
 121  
     {
 122  242
         for ( MavenProject project : reactorProjects )
 123  
         {
 124  552
             logInfo( result, "Transforming '" + project.getName() + "'..." );
 125  
 
 126  552
             transformProject( project, releaseDescriptor, releaseEnvironment, reactorProjects, simulate, result );
 127  
         }
 128  184
     }
 129  
 
 130  
     private void transformProject( MavenProject project, ReleaseDescriptor releaseDescriptor,
 131  
                                    ReleaseEnvironment releaseEnvironment, List<MavenProject> reactorProjects,
 132  
                                    boolean simulate, ReleaseResult result )
 133  
         throws ReleaseExecutionException, ReleaseFailureException
 134  
     {
 135  
         Document document;
 136  552
         String intro = null;
 137  552
         String outtro = null;
 138  
         try
 139  
         {
 140  552
             String content = ReleaseUtil.readXmlFile( ReleaseUtil.getStandardPom( project ), ls );
 141  
             // we need to eliminate any extra whitespace inside elements, as JDOM will nuke it
 142  552
             content = content.replaceAll( "<([^!][^>]*?)\\s{2,}([^>]*?)>", "<$1 $2>" );
 143  552
             content = content.replaceAll( "(\\s{2,}|[^\\s])/>", "$1 />" );
 144  
 
 145  552
             SAXBuilder builder = new SAXBuilder();
 146  552
             document = builder.build( new StringReader( content ) );
 147  
 
 148  
             // Normalize line endings to platform's style (XML processors like JDOM normalize line endings to "\n" as
 149  
             // per section 2.11 of the XML spec)
 150  552
             normaliseLineEndings( document );
 151  
 
 152  
             // rewrite DOM as a string to find differences, since text outside the root element is not tracked
 153  552
             StringWriter w = new StringWriter();
 154  552
             Format format = Format.getRawFormat();
 155  552
             format.setLineSeparator( ls );
 156  552
             XMLOutputter out = new XMLOutputter( format );
 157  552
             out.output( document.getRootElement(), w );
 158  
 
 159  552
             int index = content.indexOf( w.toString() );
 160  552
             if ( index >= 0 )
 161  
             {
 162  544
                 intro = content.substring( 0, index );
 163  544
                 outtro = content.substring( index + w.toString().length() );
 164  
             }
 165  
             else
 166  
             {
 167  
                 /*
 168  
                  * NOTE: Due to whitespace, attribute reordering or entity expansion the above indexOf test can easily
 169  
                  * fail. So let's try harder. Maybe some day, when JDOM offers a StaxBuilder and this builder employes
 170  
                  * XMLInputFactory2.P_REPORT_PROLOG_WHITESPACE, this whole mess can be avoided.
 171  
                  */
 172  8
                 final String SPACE = "\\s++";
 173  8
                 final String XML = "<\\?(?:(?:[^\"'>]++)|(?:\"[^\"]*+\")|(?:'[^\']*+'))*+>";
 174  8
                 final String INTSUB = "\\[(?:(?:[^\"'\\]]++)|(?:\"[^\"]*+\")|(?:'[^\']*+'))*+\\]";
 175  8
                 final String DOCTYPE =
 176  
                     "<!DOCTYPE(?:(?:[^\"'\\[>]++)|(?:\"[^\"]*+\")|(?:'[^\']*+')|(?:" + INTSUB + "))*+>";
 177  8
                 final String PI = XML;
 178  8
                 final String COMMENT = "<!--(?:[^-]|(?:-[^-]))*+-->";
 179  
 
 180  8
                 final String INTRO =
 181  
                     "(?:(?:" + SPACE + ")|(?:" + XML + ")|(?:" + DOCTYPE + ")|(?:" + COMMENT + ")|(?:" + PI + "))*";
 182  8
                 final String OUTRO = "(?:(?:" + SPACE + ")|(?:" + COMMENT + ")|(?:" + PI + "))*";
 183  8
                 final String POM = "(?s)(" + INTRO + ")(.*?)(" + OUTRO + ")";
 184  
 
 185  8
                 Matcher matcher = Pattern.compile( POM ).matcher( content );
 186  8
                 if ( matcher.matches() )
 187  
                 {
 188  8
                     intro = matcher.group( 1 );
 189  8
                     outtro = matcher.group( matcher.groupCount() );
 190  
                 }
 191  
             }
 192  
         }
 193  0
         catch ( JDOMException e )
 194  
         {
 195  0
             throw new ReleaseExecutionException( "Error reading POM: " + e.getMessage(), e );
 196  
         }
 197  0
         catch ( IOException e )
 198  
         {
 199  0
             throw new ReleaseExecutionException( "Error reading POM: " + e.getMessage(), e );
 200  552
         }
 201  
 
 202  
         ScmRepository scmRepository;
 203  
         ScmProvider provider;
 204  
         try
 205  
         {
 206  552
             scmRepository = scmRepositoryConfigurator.getConfiguredRepository( releaseDescriptor,
 207  
                                                                                releaseEnvironment.getSettings() );
 208  
 
 209  544
             provider = scmRepositoryConfigurator.getRepositoryProvider( scmRepository );
 210  
         }
 211  4
         catch ( ScmRepositoryException e )
 212  
         {
 213  4
             throw new ReleaseScmRepositoryException( e.getMessage(), e.getValidationMessages() );
 214  
         }
 215  4
         catch ( NoSuchScmProviderException e )
 216  
         {
 217  4
             throw new ReleaseExecutionException( "Unable to configure SCM repository: " + e.getMessage(), e );
 218  544
         }
 219  
 
 220  544
         transformDocument( project, document.getRootElement(), releaseDescriptor, reactorProjects, scmRepository,
 221  
                            result, simulate );
 222  
 
 223  502
         File pomFile = ReleaseUtil.getStandardPom( project );
 224  
 
 225  502
         if ( simulate )
 226  
         {
 227  24
             File outputFile = new File( pomFile.getParentFile(), pomFile.getName() + "." + pomSuffix );
 228  24
             writePom( outputFile, document, releaseDescriptor, project.getModelVersion(), intro, outtro );
 229  24
         }
 230  
         else
 231  
         {
 232  478
             writePom( pomFile, document, releaseDescriptor, project.getModelVersion(), intro, outtro, scmRepository,
 233  
                       provider );
 234  
         }
 235  494
     }
 236  
 
 237  
     private void normaliseLineEndings( Document document )
 238  
     {
 239  552
         for ( Iterator<?> i = document.getDescendants( new ContentFilter( ContentFilter.COMMENT ) ); i.hasNext(); )
 240  
         {
 241  790
             Comment c = (Comment) i.next();
 242  790
             c.setText( ReleaseUtil.normalizeLineEndings( c.getText(), ls ) );
 243  790
         }
 244  552
         for ( Iterator<?> i = document.getDescendants( new ContentFilter( ContentFilter.CDATA ) ); i.hasNext(); )
 245  
         {
 246  110
             CDATA c = (CDATA) i.next();
 247  110
             c.setText( ReleaseUtil.normalizeLineEndings( c.getText(), ls ) );
 248  110
         }
 249  552
     }
 250  
 
 251  
     private void transformDocument( MavenProject project, Element rootElement, ReleaseDescriptor releaseDescriptor,
 252  
                                     List<MavenProject> reactorProjects, ScmRepository scmRepository, ReleaseResult result,
 253  
                                     boolean simulate )
 254  
         throws ReleaseExecutionException, ReleaseFailureException
 255  
     {
 256  544
         Namespace namespace = rootElement.getNamespace();
 257  544
         Map<String, String> mappedVersions = getNextVersionMap( releaseDescriptor );
 258  544
         Map<String, String> originalVersions = getOriginalVersionMap( releaseDescriptor, reactorProjects, simulate );
 259  
         @SuppressWarnings("unchecked")
 260  544
                 Map<String, Map<String, String>> resolvedSnapshotDependencies = releaseDescriptor.getResolvedSnapshotDependencies();
 261  544
         Model model = project.getModel();
 262  544
         Element properties = rootElement.getChild( "properties", namespace );
 263  
 
 264  544
         String parentVersion = rewriteParent( project, rootElement, namespace, mappedVersions, 
 265  
                                               resolvedSnapshotDependencies, originalVersions );
 266  
 
 267  542
         String projectId = ArtifactUtils.versionlessKey( project.getGroupId(), project.getArtifactId() );
 268  
 
 269  542
         rewriteVersion( rootElement, namespace, mappedVersions, projectId, project, parentVersion );
 270  
 
 271  504
         List<Element> roots = new ArrayList<Element>();
 272  504
         roots.add( rootElement );
 273  504
         roots.addAll( getChildren( rootElement, "profiles", "profile" ) );
 274  
 
 275  504
         for ( Element root : roots )
 276  
         {
 277  534
             rewriteArtifactVersions( getChildren( root, "dependencies", "dependency" ), mappedVersions,
 278  
                                     resolvedSnapshotDependencies, originalVersions, model, properties, result,
 279  
                                     releaseDescriptor );
 280  
 
 281  534
             rewriteArtifactVersions( getChildren( root, "dependencyManagement", "dependencies", "dependency" ),
 282  
                                     mappedVersions, resolvedSnapshotDependencies, originalVersions, model, properties,
 283  
                                     result, releaseDescriptor );
 284  
 
 285  534
             rewriteArtifactVersions( getChildren( root, "build", "extensions", "extension" ), mappedVersions,
 286  
                                     resolvedSnapshotDependencies, originalVersions, model, properties, result,
 287  
                                     releaseDescriptor );
 288  
 
 289  534
             List<Element> pluginElements = new ArrayList<Element>();
 290  534
             pluginElements.addAll( getChildren( root, "build", "plugins", "plugin" ) );
 291  534
             pluginElements.addAll( getChildren( root, "build", "pluginManagement", "plugins", "plugin" ) );
 292  
 
 293  534
             rewriteArtifactVersions( pluginElements, mappedVersions, resolvedSnapshotDependencies, originalVersions,
 294  
                                     model, properties, result, releaseDescriptor );
 295  
 
 296  534
             for ( Element pluginElement : pluginElements )
 297  
             {
 298  130
                 rewriteArtifactVersions( getChildren( pluginElement, "dependencies", "dependency" ), mappedVersions,
 299  
                                         resolvedSnapshotDependencies, originalVersions, model, properties, result,
 300  
                                         releaseDescriptor );
 301  
             }
 302  
 
 303  534
             rewriteArtifactVersions( getChildren( root, "reporting", "plugins", "plugin" ), mappedVersions,
 304  
                                     resolvedSnapshotDependencies, originalVersions, model, properties, result,
 305  
                                     releaseDescriptor );
 306  534
         }
 307  
 
 308  
         String commonBasedir;
 309  
         try
 310  
         {
 311  504
             commonBasedir = ReleaseUtil.getCommonBasedir( reactorProjects );
 312  
         }
 313  0
         catch ( IOException e )
 314  
         {
 315  0
             throw new ReleaseExecutionException( "Exception occurred while calculating common basedir: "
 316  
                 + e.getMessage(), e );
 317  504
         }
 318  504
         transformScm( project, rootElement, namespace, releaseDescriptor, projectId, scmRepository, result,
 319  
                       commonBasedir );
 320  502
     }
 321  
 
 322  
     @SuppressWarnings( "unchecked" )
 323  
     private List<Element> getChildren( Element root, String... names )
 324  
     {
 325  3838
         Element parent = root;
 326  8142
         for ( int i = 0; i < names.length - 1 && parent != null; i++ )
 327  
         {
 328  4304
             parent = parent.getChild( names[i], parent.getNamespace() );
 329  
         }
 330  3838
         if ( parent == null )
 331  
         {
 332  3486
             return Collections.emptyList();
 333  
         }
 334  352
         return parent.getChildren( names[names.length - 1], parent.getNamespace() );
 335  
     }
 336  
 
 337  
     /**
 338  
      * Updates the text value of the given element. The primary purpose of this method is to preserve any whitespace and
 339  
      * comments around the original text value.
 340  
      *
 341  
      * @param element The element to update, must not be <code>null</code>.
 342  
      * @param value   The text string to set, must not be <code>null</code>.
 343  
      */
 344  
     private void rewriteValue( Element element, String value )
 345  
     {
 346  1028
         Text text = null;
 347  1028
         if ( element.getContent() != null )
 348  
         {
 349  1028
             for ( Iterator<?> it = element.getContent().iterator(); it.hasNext(); )
 350  
             {
 351  1068
                 Object content = it.next();
 352  1068
                 if ( ( content instanceof Text ) && ( (Text) content ).getTextTrim().length() > 0 )
 353  
                 {
 354  1028
                     text = (Text) content;
 355  1030
                     while ( it.hasNext() )
 356  
                     {
 357  34
                         content = it.next();
 358  34
                         if ( content instanceof Text )
 359  
                         {
 360  2
                             text.append( (Text) content );
 361  2
                             it.remove();
 362  
                         }
 363  
                         else
 364  
                         {
 365  
                             break;
 366  
                         }
 367  
                     }
 368  
                     break;
 369  
                 }
 370  40
             }
 371  
         }
 372  1028
         if ( text == null )
 373  
         {
 374  0
             element.addContent( value );
 375  
         }
 376  
         else
 377  
         {
 378  1028
             String chars = text.getText();
 379  1028
             String trimmed = text.getTextTrim();
 380  1028
             int idx = chars.indexOf( trimmed );
 381  1028
             String leadingWhitespace = chars.substring( 0, idx );
 382  1028
             String trailingWhitespace = chars.substring( idx + trimmed.length() );
 383  1028
             text.setText( leadingWhitespace + value + trailingWhitespace );
 384  
         }
 385  1028
     }
 386  
 
 387  
     private void rewriteVersion( Element rootElement, Namespace namespace, Map<String, String> mappedVersions, String projectId,
 388  
                                  MavenProject project, String parentVersion )
 389  
         throws ReleaseFailureException
 390  
     {
 391  542
         Element versionElement = rootElement.getChild( "version", namespace );
 392  542
         String version = mappedVersions.get( projectId );
 393  542
         if ( version == null )
 394  
         {
 395  38
             throw new ReleaseFailureException( "Version for '" + project.getName() + "' was not mapped" );
 396  
         }
 397  
 
 398  504
         if ( versionElement == null )
 399  
         {
 400  216
             if ( !version.equals( parentVersion ) )
 401  
             {
 402  
                 // we will add this after artifactId, since it was missing but different from the inherited version
 403  6
                 Element artifactIdElement = rootElement.getChild( "artifactId", namespace );
 404  6
                 int index = rootElement.indexOf( artifactIdElement );
 405  
 
 406  6
                 versionElement = new Element( "version", namespace );
 407  6
                 versionElement.setText( version );
 408  6
                 rootElement.addContent( index + 1, new Text( "\n  " ) );
 409  6
                 rootElement.addContent( index + 2, versionElement );
 410  6
             }
 411  
         }
 412  
         else
 413  
         {
 414  288
             rewriteValue( versionElement, version );
 415  
         }
 416  504
     }
 417  
 
 418  
     private String rewriteParent( MavenProject project, Element rootElement, Namespace namespace, Map<String, String> mappedVersions,
 419  
                                   Map<String, Map<String, String>> resolvedSnapshotDependencies, Map<String, String> originalVersions )
 420  
         throws ReleaseFailureException
 421  
     {
 422  544
         String parentVersion = null;
 423  544
         if ( project.hasParent() )
 424  
         {
 425  314
             Element parentElement = rootElement.getChild( "parent", namespace );
 426  314
             Element versionElement = parentElement.getChild( "version", namespace );
 427  314
             MavenProject parent = project.getParent();
 428  314
             String key = ArtifactUtils.versionlessKey( parent.getGroupId(), parent.getArtifactId() );
 429  314
             parentVersion = mappedVersions.get( key );
 430  314
             if ( parentVersion == null )
 431  
             {
 432  
                 //MRELEASE-317
 433  10
                 parentVersion = getResolvedSnapshotVersion( key, resolvedSnapshotDependencies );
 434  
             }
 435  314
             if ( parentVersion == null )
 436  
             {
 437  8
                 if ( parent.getVersion().equals( originalVersions.get( key ) ) )
 438  
                 {
 439  2
                     throw new ReleaseFailureException( "Version for parent '" + parent.getName() + "' was not mapped" );
 440  
                 }
 441  
             }
 442  
             else
 443  
             {
 444  306
                 rewriteValue( versionElement, parentVersion );
 445  
             }
 446  
         }
 447  542
         return parentVersion;
 448  
     }
 449  
 
 450  
     private void rewriteArtifactVersions( Collection<Element> elements, Map<String, String> mappedVersions,
 451  
                                           Map<String, Map<String, String>> resolvedSnapshotDependencies, Map<String, String> originalVersions,
 452  
                                           Model projectModel, Element properties, ReleaseResult result,
 453  
                                           ReleaseDescriptor releaseDescriptor )
 454  
         throws ReleaseExecutionException, ReleaseFailureException
 455  
     {
 456  2800
         if ( elements == null )
 457  
         {
 458  0
             return;
 459  
         }
 460  2800
         String projectId = ArtifactUtils.versionlessKey( projectModel.getGroupId(), projectModel.getArtifactId() );
 461  2800
         for ( Element element : elements )
 462  
         {
 463  404
             Element versionElement = element.getChild( "version", element.getNamespace() );
 464  404
             if ( versionElement == null )
 465  
             {
 466  
                 // managed dependency or unversioned plugin
 467  50
                 continue;
 468  
             }
 469  354
             String rawVersion = versionElement.getTextTrim();
 470  
 
 471  354
             Element groupIdElement = element.getChild( "groupId", element.getNamespace() );
 472  354
             if ( groupIdElement == null )
 473  
             {
 474  0
                 if ( "plugin".equals( element.getName() ) )
 475  
                 {
 476  0
                     groupIdElement = new Element( "groupId", element.getNamespace() );
 477  0
                     groupIdElement.setText( "org.apache.maven.plugins" );
 478  
                 }
 479  
                 else
 480  
                 {
 481  
                     // incomplete dependency
 482  
                     continue;
 483  
                 }
 484  
             }
 485  354
             String groupId = interpolate( groupIdElement.getTextTrim(), projectModel );
 486  
 
 487  354
             Element artifactIdElement = element.getChild( "artifactId", element.getNamespace() );
 488  354
             if ( artifactIdElement == null )
 489  
             {
 490  
                 // incomplete element
 491  0
                 continue;
 492  
             }
 493  354
             String artifactId = interpolate( artifactIdElement.getTextTrim(), projectModel);
 494  
 
 495  354
             String key = ArtifactUtils.versionlessKey( groupId, artifactId );
 496  354
             String resolvedSnapshotVersion = getResolvedSnapshotVersion( key, resolvedSnapshotDependencies );
 497  354
             String mappedVersion = mappedVersions.get( key );
 498  354
             String originalVersion = originalVersions.get( key );
 499  354
             if ( originalVersion == null )
 500  
             {
 501  56
                 originalVersion = getOriginalResolvedSnapshotVersion( key, resolvedSnapshotDependencies );
 502  
             }
 503  
 
 504  
             // MRELEASE-220
 505  354
             if ( mappedVersion != null && mappedVersion.endsWith( Artifact.SNAPSHOT_VERSION )
 506  
                 && !rawVersion.endsWith( Artifact.SNAPSHOT_VERSION ) && !releaseDescriptor.isUpdateDependencies() )
 507  
             {
 508  8
                 continue;
 509  
             }
 510  
 
 511  346
             if ( mappedVersion != null )
 512  
             {
 513  254
                 if ( rawVersion.equals( originalVersion ) )
 514  
                 {
 515  142
                     logInfo( result, "  Updating " + artifactId + " to " + mappedVersion );
 516  142
                     rewriteValue( versionElement, mappedVersion );
 517  
                 }
 518  112
                 else if ( rawVersion.matches( "\\$\\{.+\\}" ) )
 519  
                 {
 520  38
                     String expression = rawVersion.substring( 2, rawVersion.length() - 1 );
 521  
 
 522  38
                     if ( expression.startsWith( "project." ) || expression.startsWith( "pom." )
 523  
                         || "version".equals( expression ) )
 524  
                     {
 525  28
                         if ( !mappedVersion.equals( mappedVersions.get( projectId ) ) )
 526  
                         {
 527  12
                             logInfo( result, "  Updating " + artifactId + " to " + mappedVersion );
 528  12
                             rewriteValue( versionElement, mappedVersion );
 529  
                         }
 530  
                         else
 531  
                         {
 532  16
                             logInfo( result, "  Ignoring artifact version update for expression " + rawVersion );
 533  
                         }
 534  
                     }
 535  10
                     else if ( properties != null )
 536  
                     {
 537  
                         // version is an expression, check for properties to update instead
 538  10
                         Element property = properties.getChild( expression, properties.getNamespace() );
 539  10
                         if ( property != null )
 540  
                         {
 541  10
                             String propertyValue = property.getTextTrim();
 542  
 
 543  10
                             if ( propertyValue.equals( originalVersion ) )
 544  
                             {
 545  10
                                 logInfo( result, "  Updating " + rawVersion + " to " + mappedVersion );
 546  
                                 // change the property only if the property is the same as what's in the reactor
 547  10
                                 rewriteValue( property, mappedVersion );
 548  
                             }
 549  0
                             else if ( mappedVersion.equals( propertyValue ) )
 550  
                             {
 551  
                                 // this property may have been updated during processing a sibling.
 552  0
                                 logInfo( result, "  Ignoring artifact version update for expression " + rawVersion
 553  
                                     + " because it is already updated" );
 554  
                             }
 555  0
                             else if ( !mappedVersion.equals( rawVersion ) )
 556  
                             {
 557  0
                                 if ( mappedVersion.matches( "\\$\\{project.+\\}" )
 558  
                                     || mappedVersion.matches( "\\$\\{pom.+\\}" ) || "${version}".equals( mappedVersion ) )
 559  
                                 {
 560  0
                                     logInfo( result, "  Ignoring artifact version update for expression "
 561  
                                         + mappedVersion );
 562  
                                     // ignore... we cannot update this expression
 563  
                                 }
 564  
                                 else
 565  
                                 {
 566  
                                     // the value of the expression conflicts with what the user wanted to release
 567  0
                                     throw new ReleaseFailureException( "The artifact (" + key + ") requires a "
 568  
                                         + "different version (" + mappedVersion + ") than what is found ("
 569  
                                         + propertyValue + ") for the expression (" + expression + ") in the "
 570  
                                         + "project (" + projectId + ")." );
 571  
                                 }
 572  
                             }
 573  10
                         }
 574  
                         else
 575  
                         {
 576  
                             // the expression used to define the version of this artifact may be inherited
 577  
                             // TODO needs a better error message, what pom? what dependency?
 578  0
                             throw new ReleaseFailureException( "The version could not be updated: " + rawVersion );
 579  
                         }
 580  
                     }
 581  38
                 }
 582  
                 else
 583  
                 {
 584  
                     // different/previous version not related to current release
 585  
                 }
 586  
             }
 587  92
             else if ( resolvedSnapshotVersion != null )
 588  
             {
 589  0
                 logInfo( result, "  Updating " + artifactId + " to " + resolvedSnapshotVersion );
 590  
 
 591  0
                 rewriteValue( versionElement, resolvedSnapshotVersion );
 592  
             }
 593  
             else
 594  
             {
 595  
                 // artifact not related to current release
 596  
             }
 597  346
         }
 598  2800
     }
 599  
 
 600  
     private String interpolate( String value, Model model )
 601  
         throws ReleaseExecutionException
 602  
     {
 603  708
         if ( value != null && value.contains( "${" ) )
 604  
         {
 605  4
             StringSearchInterpolator interpolator = new StringSearchInterpolator();
 606  4
             List<String> pomPrefixes = Arrays.asList( "pom.", "project." );
 607  4
             interpolator.addValueSource( new PrefixedObjectValueSource( pomPrefixes, model, false ) );
 608  4
             interpolator.addValueSource( new MapBasedValueSource( model.getProperties() ) );
 609  4
             interpolator.addValueSource( new ObjectBasedValueSource( model ) );
 610  
             try
 611  
             {
 612  4
                 value = interpolator.interpolate( value, new PrefixAwareRecursionInterceptor( pomPrefixes ) );
 613  
             }
 614  0
             catch ( InterpolationException e )
 615  
             {
 616  0
                 throw new ReleaseExecutionException(
 617  
                                                      "Failed to interpolate " + value + " for project " + model.getId(),
 618  
                                                      e );
 619  4
             }
 620  
         }
 621  708
         return value;
 622  
     }
 623  
 
 624  
     private void writePom( File pomFile, Document document, ReleaseDescriptor releaseDescriptor, String modelVersion,
 625  
                            String intro, String outtro, ScmRepository repository, ScmProvider provider )
 626  
         throws ReleaseExecutionException, ReleaseScmCommandException
 627  
     {
 628  
         try
 629  
         {
 630  478
             if ( releaseDescriptor.isScmUseEditMode() || provider.requiresEditMode() )
 631  
             {
 632  12
                 EditScmResult result = provider.edit( repository, new ScmFileSet(
 633  
                     new File( releaseDescriptor.getWorkingDirectory() ), pomFile ) );
 634  
 
 635  8
                 if ( !result.isSuccess() )
 636  
                 {
 637  4
                     throw new ReleaseScmCommandException( "Unable to enable editing on the POM", result );
 638  
                 }
 639  
             }
 640  
         }
 641  4
         catch ( ScmException e )
 642  
         {
 643  4
             throw new ReleaseExecutionException( "An error occurred enabling edit mode: " + e.getMessage(), e );
 644  470
         }
 645  
 
 646  470
         writePom( pomFile, document, releaseDescriptor, modelVersion, intro, outtro );
 647  470
     }
 648  
 
 649  
     private void writePom( File pomFile, Document document, ReleaseDescriptor releaseDescriptor, String modelVersion,
 650  
                            String intro, String outtro )
 651  
         throws ReleaseExecutionException
 652  
     {
 653  494
         Element rootElement = document.getRootElement();
 654  
 
 655  494
         if ( releaseDescriptor.isAddSchema() )
 656  
         {
 657  8
             Namespace pomNamespace = Namespace.getNamespace( "", "http://maven.apache.org/POM/" + modelVersion );
 658  8
             rootElement.setNamespace( pomNamespace );
 659  8
             Namespace xsiNamespace = Namespace.getNamespace( "xsi", "http://www.w3.org/2001/XMLSchema-instance" );
 660  8
             rootElement.addNamespaceDeclaration( xsiNamespace );
 661  
 
 662  8
             if ( rootElement.getAttribute( "schemaLocation", xsiNamespace ) == null )
 663  
             {
 664  8
                 rootElement.setAttribute( "schemaLocation", "http://maven.apache.org/POM/" + modelVersion
 665  
                     + " http://maven.apache.org/maven-v" + modelVersion.replace( '.', '_' ) + ".xsd", xsiNamespace );
 666  
             }
 667  
 
 668  
             // the empty namespace is considered equal to the POM namespace, so match them up to avoid extra xmlns=""
 669  8
             ElementFilter elementFilter = new ElementFilter( Namespace.getNamespace( "" ) );
 670  8
             for ( Iterator<?> i = rootElement.getDescendants( elementFilter ); i.hasNext(); )
 671  
             {
 672  80
                 Element e = (Element) i.next();
 673  80
                 e.setNamespace( pomNamespace );
 674  80
             }
 675  
         }
 676  
 
 677  494
         Writer writer = null;
 678  
         try
 679  
         {
 680  494
             writer = WriterFactory.newXmlWriter( pomFile );
 681  
 
 682  494
             if ( intro != null )
 683  
             {
 684  494
                 writer.write( intro );
 685  
             }
 686  
 
 687  494
             Format format = Format.getRawFormat();
 688  494
             format.setLineSeparator( ls );
 689  494
             XMLOutputter out = new XMLOutputter( format );
 690  494
             out.output( document.getRootElement(), writer );
 691  
 
 692  494
             if ( outtro != null )
 693  
             {
 694  494
                 writer.write( outtro );
 695  
             }
 696  
         }
 697  0
         catch ( IOException e )
 698  
         {
 699  0
             throw new ReleaseExecutionException( "Error writing POM: " + e.getMessage(), e );
 700  
         }
 701  
         finally
 702  
         {
 703  494
             IOUtil.close( writer );
 704  494
         }
 705  494
     }
 706  
 
 707  
     public ReleaseResult simulate( ReleaseDescriptor releaseDescriptor, ReleaseEnvironment releaseEnvironment,
 708  
                                    List<MavenProject> reactorProjects )
 709  
         throws ReleaseExecutionException, ReleaseFailureException
 710  
     {
 711  18
         ReleaseResult result = new ReleaseResult();
 712  
 
 713  18
         transform( releaseDescriptor, releaseEnvironment, reactorProjects, true, result );
 714  
 
 715  18
         result.setResultCode( ReleaseResult.SUCCESS );
 716  
 
 717  18
         return result;
 718  
     }
 719  
 
 720  
     public ReleaseResult clean( List<MavenProject> reactorProjects )
 721  
     {
 722  12
         ReleaseResult result = new ReleaseResult();
 723  
 
 724  12
         super.clean( reactorProjects );
 725  
 
 726  12
         if ( reactorProjects != null )
 727  
         {
 728  12
             for ( MavenProject project : reactorProjects )
 729  
             {
 730  8
                 File pomFile = ReleaseUtil.getStandardPom( project );
 731  
                 // MRELEASE-273 : if no pom
 732  8
                 if ( pomFile != null )
 733  
                 {
 734  8
                     File file = new File( pomFile.getParentFile(), pomFile.getName() + "." + pomSuffix );
 735  8
                     if ( file.exists() )
 736  
                     {
 737  4
                         file.delete();
 738  
                     }
 739  
                 }
 740  8
             }
 741  
         }
 742  
 
 743  12
         result.setResultCode( ReleaseResult.SUCCESS );
 744  
 
 745  12
         return result;
 746  
     }
 747  
 
 748  
     protected abstract String getResolvedSnapshotVersion( String artifactVersionlessKey, Map<String, Map<String,String>> resolvedSnapshots );
 749  
 
 750  
     protected abstract Map<String, String> getOriginalVersionMap( ReleaseDescriptor releaseDescriptor, List<MavenProject> reactorProjects,
 751  
                                                   boolean simulate );
 752  
 
 753  
     protected abstract Map<String,String> getNextVersionMap( ReleaseDescriptor releaseDescriptor );
 754  
 
 755  
     protected abstract void transformScm( MavenProject project, Element rootElement, Namespace namespace,
 756  
                                           ReleaseDescriptor releaseDescriptor, String projectId,
 757  
                                           ScmRepository scmRepository, ReleaseResult result, String commonBasedir )
 758  
         throws ReleaseExecutionException;
 759  
 
 760  
     protected String getOriginalResolvedSnapshotVersion( String artifactVersionlessKey, Map<String, Map<String, String>> resolvedSnapshots )
 761  
     {
 762  56
         Map<String, String> versionsMap = resolvedSnapshots.get( artifactVersionlessKey );
 763  
 
 764  56
         if ( versionsMap != null )
 765  
         {
 766  0
             return versionsMap.get( ReleaseDescriptor.ORIGINAL_VERSION );
 767  
         }
 768  
         else
 769  
         {
 770  56
             return null;
 771  
         }
 772  
     }
 773  
 
 774  
     protected Element rewriteElement( String name, String value, Element root, Namespace namespace )
 775  
     {
 776  324
         Element tagElement = root.getChild( name, namespace );
 777  324
         if ( tagElement != null )
 778  
         {
 779  274
             if ( value != null )
 780  
             {
 781  270
                 rewriteValue( tagElement, value );
 782  
             }
 783  
             else
 784  
             {
 785  4
                 int index = root.indexOf( tagElement );
 786  4
                 root.removeContent( index );
 787  8
                 for ( int i = index - 1; i >= 0; i-- )
 788  
                 {
 789  8
                     if ( root.getContent( i ) instanceof Text )
 790  
                     {
 791  4
                         root.removeContent( i );
 792  
                     }
 793  
                     else
 794  
                     {
 795  
                         break;
 796  
                     }
 797  
                 }
 798  4
             }
 799  
         }
 800  
         else
 801  
         {
 802  50
             if ( value != null )
 803  
             {
 804  14
                 Element element = new Element( name, namespace );
 805  14
                 element.setText( value );
 806  14
                 root.addContent( "  " ).addContent( element ).addContent( "\n  " );
 807  14
                 tagElement = element;
 808  
             }
 809  
         }
 810  324
         return tagElement;
 811  
     }
 812  
  
 813  
     protected Scm buildScm( MavenProject project )
 814  
     {
 815  
         IdentifiedScm scm;
 816  62
         if ( project.getOriginalModel().getScm() == null )
 817  
         {
 818  0
             scm = null;
 819  
         }
 820  
         else
 821  
         {
 822  62
             scm = new IdentifiedScm();
 823  62
             scm.setConnection( project.getOriginalModel().getScm().getConnection() );
 824  62
             scm.setDeveloperConnection( project.getOriginalModel().getScm().getDeveloperConnection() );
 825  62
             scm.setTag( project.getOriginalModel().getScm().getTag() );
 826  62
             scm.setUrl( project.getOriginalModel().getScm().getUrl() );
 827  62
             scm.setId( project.getProperties().getProperty( "project.scm.id" ) );
 828  
         }
 829  62
         return scm;
 830  
     }
 831  
 }