Coverage Report - org.apache.maven.scm.ChangeSet
 
Classes in this File Line Coverage Branch Coverage Complexity
ChangeSet
64 %
111/173
60 %
50/82
2,839
 
 1  
 package org.apache.maven.scm;
 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.Serializable;
 23  
 import java.text.ParseException;
 24  
 import java.text.SimpleDateFormat;
 25  
 import java.util.ArrayList;
 26  
 import java.util.Collections;
 27  
 import java.util.Date;
 28  
 import java.util.LinkedHashSet;
 29  
 import java.util.List;
 30  
 import java.util.Set;
 31  
 
 32  
 import org.apache.maven.scm.provider.ScmProviderRepository;
 33  
 import org.apache.maven.scm.util.FilenameUtils;
 34  
 import org.apache.maven.scm.util.ThreadSafeDateFormat;
 35  
 import org.codehaus.plexus.util.StringUtils;
 36  
 
 37  
 /**
 38  
  * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
 39  
  * @version $Id: ChangeSet.java 1294391 2012-02-27 23:16:22Z hboutemy $
 40  
  */
 41  
 public class ChangeSet
 42  
     implements Serializable
 43  
 {
 44  
     /**
 45  
      *
 46  
      */
 47  
     private static final long serialVersionUID = 7097705862222539801L;
 48  
 
 49  
     /**
 50  
      * Escaped <code>&lt;</code> entity
 51  
      */
 52  
     public static final String LESS_THAN_ENTITY = "&lt;";
 53  
 
 54  
     /**
 55  
      * Escaped <code>&gt;</code> entity
 56  
      */
 57  
     public static final String GREATER_THAN_ENTITY = "&gt;";
 58  
 
 59  
     /**
 60  
      * Escaped <code>&amp;</code> entity
 61  
      */
 62  
     public static final String AMPERSAND_ENTITY = "&amp;";
 63  
 
 64  
     /**
 65  
      * Escaped <code>'</code> entity
 66  
      */
 67  
     public static final String APOSTROPHE_ENTITY = "&apos;";
 68  
 
 69  
     /**
 70  
      * Escaped <code>"</code> entity
 71  
      */
 72  
     public static final String QUOTE_ENTITY = "&quot;";
 73  
 
 74  
     private static final String DATE_PATTERN = "yyyy-MM-dd";
 75  
 
 76  
     /**
 77  
      * Formatter used by the getDateFormatted method.
 78  
      */
 79  1
     private static final ThreadSafeDateFormat DATE_FORMAT = new ThreadSafeDateFormat( DATE_PATTERN );
 80  
 
 81  
     private static final String TIME_PATTERN = "HH:mm:ss";
 82  
 
 83  
     /**
 84  
      * Formatter used by the getTimeFormatted method.
 85  
      */
 86  1
     private static final ThreadSafeDateFormat TIME_FORMAT = new ThreadSafeDateFormat( TIME_PATTERN );
 87  
 
 88  
     /**
 89  
      * Formatter used to parse date/timestamp.
 90  
      */
 91  1
     private static final ThreadSafeDateFormat TIMESTAMP_FORMAT_1 = new ThreadSafeDateFormat( "yyyy/MM/dd HH:mm:ss" );
 92  
 
 93  1
     private static final ThreadSafeDateFormat TIMESTAMP_FORMAT_2 = new ThreadSafeDateFormat( "yyyy-MM-dd HH:mm:ss" );
 94  
 
 95  1
     private static final ThreadSafeDateFormat TIMESTAMP_FORMAT_3 = new ThreadSafeDateFormat( "yyyy/MM/dd HH:mm:ss z" );
 96  
 
 97  1
     private static final ThreadSafeDateFormat TIMESTAMP_FORMAT_4 = new ThreadSafeDateFormat( "yyyy-MM-dd HH:mm:ss z" );
 98  
 
 99  
     /**
 100  
      * Date the changes were committed
 101  
      */
 102  
     private Date date;
 103  
 
 104  
     /**
 105  
      * User who made changes
 106  
      */
 107  
     private String author;
 108  
 
 109  
     /**
 110  
      * comment provided at commit time
 111  
      */
 112  17
     private String comment = "";
 113  
 
 114  
     /**
 115  
      * List of ChangeFile
 116  
      */
 117  
     private List<ChangeFile> files;
 118  
     
 119  
     /**
 120  
      * The SCM revision id for this changeset.
 121  
      * @since 1.3
 122  
      */
 123  
     private String revision;
 124  
 
 125  
     /**
 126  
      * Revision from which this one originates
 127  
      * @since 1.7
 128  
      */
 129  
     private String parentRevision;
 130  
 
 131  
     /**
 132  
      * Revisions that were merged into this one
 133  
      * @since 1.7
 134  
      */
 135  
     private Set<String> mergedRevisions;
 136  
 
 137  
     /**
 138  
      * @param strDate         Date the changes were committed
 139  
      * @param userDatePattern pattern of date
 140  
      * @param comment         comment provided at commit time
 141  
      * @param author          User who made changes
 142  
      * @param files           The ChangeFile list
 143  
      */
 144  
     public ChangeSet( String strDate, String userDatePattern, String comment, String author,
 145  
                       List<ChangeFile> files )
 146  
     {
 147  0
         this( null, comment, author, files );
 148  
 
 149  0
         setDate( strDate, userDatePattern );
 150  0
     }
 151  
 
 152  
     /**
 153  
      * @param date    Date the changes were committed
 154  
      * @param comment comment provided at commit time
 155  
      * @param author  User who made changes
 156  
      * @param files   The ChangeFile list
 157  
      */
 158  
     public ChangeSet( Date date, String comment, String author, List<ChangeFile> files )
 159  0
     {
 160  0
         setDate( date );
 161  
 
 162  0
         setAuthor( author );
 163  
 
 164  0
         setComment( comment );
 165  
 
 166  0
         this.files = files;
 167  0
     }
 168  
 
 169  
     /**
 170  
      * Constructor used when attributes aren't available until later
 171  
      */
 172  
     public ChangeSet()
 173  17
     {
 174  
         // no op
 175  17
     }
 176  
 
 177  
     /**
 178  
      * Getter for ChangeFile list.
 179  
      *
 180  
      * @return List of ChangeFile.
 181  
      */
 182  
     public List<ChangeFile> getFiles()
 183  
     {
 184  0
         if ( files == null )
 185  
         {
 186  0
             return new ArrayList<ChangeFile>();
 187  
         }
 188  0
         return files;
 189  
     }
 190  
 
 191  
     /**
 192  
      * Setter for ChangeFile list.
 193  
      *
 194  
      * @param files List of ChangeFiles.
 195  
      */
 196  
     public void setFiles( List<ChangeFile> files )
 197  
     {
 198  0
         this.files = files;
 199  0
     }
 200  
 
 201  
     public void addFile( ChangeFile file )
 202  
     {
 203  3
         if ( files == null )
 204  
         {
 205  2
             files = new ArrayList<ChangeFile>();
 206  
         }
 207  
 
 208  3
         files.add( file );
 209  3
     }
 210  
 
 211  
     /**
 212  
      * @deprecated Use method {@link #containsFilename(String)}
 213  
      * @param filename
 214  
      * @param repository NOT USED
 215  
      * @return
 216  
      */
 217  
     public boolean containsFilename( String filename, ScmProviderRepository repository )
 218  
     {
 219  0
         return containsFilename( filename );
 220  
     }
 221  
 
 222  
     public boolean containsFilename( String filename )
 223  
     {
 224  5
         if ( files != null )
 225  
         {
 226  5
             for ( ChangeFile file : files )
 227  
             {
 228  5
                 String f1 = FilenameUtils.normalizeFilename( file.getName() );
 229  5
                 String f2 = FilenameUtils.normalizeFilename( filename );
 230  5
                 if ( f1.indexOf( f2 ) >= 0 )
 231  
                 {
 232  4
                     return true;
 233  
                 }
 234  1
             }
 235  
         }
 236  
 
 237  1
         return false;
 238  
     }
 239  
 
 240  
     /**
 241  
      * Getter for property author.
 242  
      *
 243  
      * @return Value of property author.
 244  
      */
 245  
     public String getAuthor()
 246  
     {
 247  2
         return author;
 248  
     }
 249  
 
 250  
     /**
 251  
      * Setter for property author.
 252  
      *
 253  
      * @param author New value of property author.
 254  
      */
 255  
     public void setAuthor( String author )
 256  
     {
 257  20
         this.author = author;
 258  20
     }
 259  
 
 260  
     /**
 261  
      * Getter for property comment.
 262  
      *
 263  
      * @return Value of property comment.
 264  
      */
 265  
     public String getComment()
 266  
     {
 267  2
         return comment;
 268  
     }
 269  
 
 270  
     /**
 271  
      * Setter for property comment.
 272  
      *
 273  
      * @param comment New value of property comment.
 274  
      */
 275  
     public void setComment( String comment )
 276  
     {
 277  20
         this.comment = comment;
 278  20
     }
 279  
 
 280  
     /**
 281  
      * Getter for property date.
 282  
      *
 283  
      * @return Value of property date.
 284  
      */
 285  
     public Date getDate()
 286  
     {
 287  11
         if ( date != null )
 288  
         {
 289  11
             return (Date) date.clone();
 290  
         }
 291  
 
 292  0
         return null;
 293  
     }
 294  
 
 295  
     /**
 296  
      * Setter for property date.
 297  
      *
 298  
      * @param date New value of property date.
 299  
      */
 300  
     public void setDate( Date date )
 301  
     {
 302  1
         if ( date != null )
 303  
         {
 304  1
             this.date = new Date( date.getTime() );
 305  
         }
 306  1
     }
 307  
 
 308  
     /**
 309  
      * Setter for property date that takes a string and parses it
 310  
      *
 311  
      * @param date - a string in yyyy/MM/dd HH:mm:ss format
 312  
      */
 313  
     public void setDate( String date )
 314  
     {
 315  18
         setDate( date, null );
 316  18
     }
 317  
 
 318  
     /**
 319  
      * Setter for property date that takes a string and parses it
 320  
      *
 321  
      * @param date            - a string in yyyy/MM/dd HH:mm:ss format
 322  
      * @param userDatePattern - pattern of date
 323  
      */
 324  
     public void setDate( String date, String userDatePattern )
 325  
     {
 326  
         try
 327  
         {
 328  18
             if ( !StringUtils.isEmpty( userDatePattern ) )
 329  
             {
 330  0
                 SimpleDateFormat format = new SimpleDateFormat( userDatePattern );
 331  
 
 332  0
                 this.date = format.parse( date );
 333  0
             }
 334  
             else
 335  
             {
 336  18
                 this.date = TIMESTAMP_FORMAT_3.parse( date );
 337  
             }
 338  
         }
 339  18
         catch ( ParseException e )
 340  
         {
 341  18
             if ( !StringUtils.isEmpty( userDatePattern ) )
 342  
             {
 343  
                 try
 344  
                 {
 345  0
                     this.date = TIMESTAMP_FORMAT_3.parse( date );
 346  
                 }
 347  0
                 catch ( ParseException pe )
 348  
                 {
 349  
                     try
 350  
                     {
 351  0
                         this.date = TIMESTAMP_FORMAT_4.parse( date );
 352  
                     }
 353  0
                     catch ( ParseException pe1 )
 354  
                     {
 355  
                         try
 356  
                         {
 357  0
                             this.date = TIMESTAMP_FORMAT_1.parse( date );
 358  
                         }
 359  0
                         catch ( ParseException pe2 )
 360  
                         {
 361  
                             try
 362  
                             {
 363  0
                                 this.date = TIMESTAMP_FORMAT_2.parse( date );
 364  
                             }
 365  0
                             catch ( ParseException pe3 )
 366  
                             {
 367  0
                                 throw new IllegalArgumentException( "Unable to parse date: " + date );
 368  0
                             }
 369  0
                         }
 370  0
                     }
 371  0
                 }
 372  
             }
 373  
             else
 374  
             {
 375  
                 try
 376  
                 {
 377  18
                     this.date = TIMESTAMP_FORMAT_4.parse( date );
 378  
                 }
 379  18
                 catch ( ParseException pe1 )
 380  
                 {
 381  
                     try
 382  
                     {
 383  18
                         this.date = TIMESTAMP_FORMAT_1.parse( date );
 384  
                     }
 385  0
                     catch ( ParseException pe2 )
 386  
                     {
 387  
                         try
 388  
                         {
 389  0
                             this.date = TIMESTAMP_FORMAT_2.parse( date );
 390  
                         }
 391  0
                         catch ( ParseException pe3 )
 392  
                         {
 393  0
                             throw new IllegalArgumentException( "Unable to parse date: " + date );
 394  0
                         }
 395  18
                     }
 396  0
                 }
 397  
             }
 398  0
         }
 399  18
     }
 400  
 
 401  
     /**
 402  
      * @return date in yyyy-mm-dd format
 403  
      */
 404  
     public String getDateFormatted()
 405  
     {
 406  3
         return DATE_FORMAT.format( getDate() );
 407  
     }
 408  
 
 409  
     /**
 410  
      * @return time in HH:mm:ss format
 411  
      */
 412  
     public String getTimeFormatted()
 413  
     {
 414  3
         return TIME_FORMAT.format( getDate() );
 415  
     }
 416  
 
 417  
     /**
 418  
      * @return
 419  
      * @since 1.3
 420  
      */
 421  
     public String getRevision()
 422  
     {
 423  0
         return revision;
 424  
     }
 425  
 
 426  
     /**
 427  
      * @param revision
 428  
      * @since 1.3
 429  
      */
 430  
     public void setRevision( String revision )
 431  
     {
 432  0
         this.revision = revision;
 433  0
     }
 434  
 
 435  
     public String getParentRevision()
 436  
     {
 437  0
         return parentRevision;
 438  
     }
 439  
 
 440  
     public void setParentRevision( String parentRevision )
 441  
     {
 442  0
         this.parentRevision = parentRevision;
 443  0
     }
 444  
 
 445  
     public void addMergedRevision( String mergedRevision )
 446  
     {
 447  0
         if ( mergedRevisions == null )
 448  
         {
 449  0
             mergedRevisions = new LinkedHashSet<String>();
 450  
         }
 451  0
         mergedRevisions.add( mergedRevision );
 452  0
     }
 453  
 
 454  
     public Set<String> getMergedRevisions()
 455  
     {
 456  2
         return mergedRevisions == null ? Collections.<String> emptySet() : mergedRevisions;
 457  
     }
 458  
 
 459  
     public void setMergedRevisions( Set<String> mergedRevisions )
 460  
     {
 461  0
         this.mergedRevisions = mergedRevisions;
 462  0
     }
 463  
 
 464  
     /** {@inheritDoc} */
 465  
     public String toString()
 466  
     {
 467  8
         StringBuilder result = new StringBuilder( author == null ? " null " : author );
 468  8
         result.append( "\n" ).append( date == null ? "null " : date.toString() ).append( "\n" );
 469  
         // parent(s)
 470  8
         if ( parentRevision != null )
 471  
         {
 472  0
             result.append( "parent: " ).append( parentRevision );
 473  0
             if ( !mergedRevisions.isEmpty() )
 474  
             {
 475  0
                 result.append( " + " );
 476  0
                 result.append( mergedRevisions );
 477  
             }
 478  0
             result.append( "\n" );
 479  
         }
 480  8
         if ( files != null )
 481  
         {
 482  1
             for ( ChangeFile file : files )
 483  
             {
 484  1
                 result.append( file == null ? " null " : file.toString() ).append( "\n" );
 485  
             }
 486  
         }
 487  
 
 488  8
         result.append( comment == null ? " null " : comment );
 489  
 
 490  8
         return result.toString();
 491  
     }
 492  
 
 493  
     /**
 494  
      * Provide the changelog entry as an XML snippet.
 495  
      *
 496  
      * @return a changelog-entry in xml format
 497  
      * @task make sure comment doesn't contain CDATA tags - MAVEN114
 498  
      */
 499  
     public String toXML()
 500  
     {
 501  2
         StringBuilder buffer = new StringBuilder("\t<changelog-entry>\n" );
 502  
 
 503  2
         if ( getDate() != null )
 504  
         {
 505  2
             buffer.append( "\t\t<date pattern=\"" + DATE_PATTERN + "\">" )
 506  
                 .append( getDateFormatted() )
 507  
                 .append( "</date>\n" )
 508  
                 .append( "\t\t<time pattern=\"" + TIME_PATTERN + "\">" )
 509  
                 .append( getTimeFormatted() )
 510  
                 .append( "</time>\n" );
 511  
         }
 512  
 
 513  2
         buffer.append( "\t\t<author><![CDATA[" )
 514  
             .append( author )
 515  
             .append( "]]></author>\n" );
 516  
 
 517  2
         if ( parentRevision != null )
 518  
         {
 519  0
             buffer.append( "\t\t<parent>" ).append( getParentRevision() ).append( "</parent>\n" );
 520  
         }
 521  2
         for ( String mergedRevision : getMergedRevisions() )
 522  
         {
 523  0
             buffer.append( "\t\t<merge>" ).append( mergedRevision ).append( "</merge>\n" );
 524  
         }
 525  
 
 526  2
         if ( files != null )
 527  
         {
 528  1
             for ( ChangeFile file : files )
 529  
             {
 530  2
                 buffer.append( "\t\t<file>\n" );
 531  2
                 if ( file.getAction() != null )
 532  
                 {
 533  0
                     buffer.append( "\t\t\t<action>" ).append( file.getAction() ).append( "</action>\n" );
 534  
                 }
 535  2
                 buffer.append( "\t\t\t<name>" ).append( escapeValue( file.getName() ) ).append( "</name>\n" );
 536  2
                 buffer.append( "\t\t\t<revision>" ).append( file.getRevision() ).append( "</revision>\n" );
 537  2
                 if ( file.getOriginalName() != null )
 538  
                 {
 539  0
                     buffer.append( "\t\t\t<orig-name>" ).append( escapeValue( file.getOriginalName() ) ).append( "</orig-name>\n" );
 540  
                 }
 541  2
                 if ( file.getOriginalRevision() != null )
 542  
                 {
 543  0
                     buffer.append( "\t\t\t<orig-revision>" ).append( file.getOriginalRevision() ).append( "</orig-revision>\n" );
 544  
                 }
 545  2
                 buffer.append( "\t\t</file>\n" );
 546  
             }
 547  
         }
 548  2
         buffer.append( "\t\t<msg><![CDATA[" )
 549  
             .append( removeCDataEnd( comment ) )
 550  
             .append( "]]></msg>\n" );
 551  2
         buffer.append( "\t</changelog-entry>\n" );
 552  
 
 553  2
         return buffer.toString();
 554  
     }
 555  
 
 556  
     /** {@inheritDoc} */
 557  
     public boolean equals( Object obj )
 558  
     {
 559  3
         if ( obj instanceof ChangeSet )
 560  
         {
 561  3
             ChangeSet changeSet = (ChangeSet) obj;
 562  
 
 563  3
             if ( toString().equals( changeSet.toString() ) )
 564  
             {
 565  2
                 return true;
 566  
             }
 567  
         }
 568  
 
 569  1
         return false;
 570  
     }
 571  
 
 572  
     /** {@inheritDoc} */
 573  
     public int hashCode()
 574  
     {
 575  3
         final int prime = 31;
 576  3
         int result = 1;
 577  3
         result = prime * result + ( ( author == null ) ? 0 : author.hashCode() );
 578  3
         result = prime * result + ( ( comment == null ) ? 0 : comment.hashCode() );
 579  3
         result = prime * result + ( ( date == null ) ? 0 : date.hashCode() );
 580  3
         result = prime * result + ( ( parentRevision == null ) ? 0 : parentRevision.hashCode() );
 581  3
         result = prime * result + ( ( mergedRevisions == null ) ? 0 : mergedRevisions.hashCode() );
 582  3
         result = prime * result + ( ( files == null ) ? 0 : files.hashCode() );
 583  3
         return result;
 584  
     }
 585  
 
 586  
     /**
 587  
      * remove a <code>]]></code> from comments (replace it with <code>] ] ></code>).
 588  
      *
 589  
      * @param message The message to modify
 590  
      * @return a clean string
 591  
      */
 592  
     private String removeCDataEnd( String message )
 593  
     {
 594  
         // check for invalid sequence ]]>
 595  
         int endCdata;
 596  2
         while ( message != null && ( endCdata = message.indexOf( "]]>" ) ) > -1 )
 597  
         {
 598  0
             message = message.substring( 0, endCdata ) + "] ] >" + message.substring( endCdata + 3, message.length() );
 599  
         }
 600  2
         return message;
 601  
     }
 602  
 
 603  
     /**
 604  
      * <p>Escape the <code>toString</code> of the given object.
 605  
      * For use in an attribute value.</p>
 606  
      * <p/>
 607  
      * swiped from jakarta-commons/betwixt -- XMLUtils.java
 608  
      *
 609  
      * @param value escape <code>value.toString()</code>
 610  
      * @return text with characters restricted (for use in attributes) escaped
 611  
      */
 612  
     public static String escapeValue( Object value )
 613  
     {
 614  9
         StringBuilder buffer = new StringBuilder( value.toString() );
 615  52
         for ( int i = 0, size = buffer.length(); i < size; i++ )
 616  
         {
 617  43
             switch ( buffer.charAt( i ) )
 618  
             {
 619  
                 case'<':
 620  2
                     buffer.replace( i, i + 1, LESS_THAN_ENTITY );
 621  2
                     size += 3;
 622  2
                     i += 3;
 623  2
                     break;
 624  
                 case'>':
 625  2
                     buffer.replace( i, i + 1, GREATER_THAN_ENTITY );
 626  2
                     size += 3;
 627  2
                     i += 3;
 628  2
                     break;
 629  
                 case'&':
 630  1
                     buffer.replace( i, i + 1, AMPERSAND_ENTITY );
 631  1
                     size += 4;
 632  1
                     i += 4;
 633  1
                     break;
 634  
                 case'\'':
 635  5
                     buffer.replace( i, i + 1, APOSTROPHE_ENTITY );
 636  5
                     size += 5;
 637  5
                     i += 5;
 638  5
                     break;
 639  
                 case'\"':
 640  1
                     buffer.replace( i, i + 1, QUOTE_ENTITY );
 641  1
                     size += 5;
 642  1
                     i += 5;
 643  
                     break;
 644  
             }
 645  
         }
 646  9
         return buffer.toString();
 647  
     }
 648  
 }