View Javadoc
1   package org.apache.maven.scm.provider.git.gitexe.command.blame;
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 org.apache.maven.scm.command.blame.BlameLine;
23  import org.apache.maven.scm.log.ScmLogger;
24  import org.apache.maven.scm.util.AbstractConsumer;
25  
26  import java.text.DateFormat;
27  import java.text.SimpleDateFormat;
28  import java.util.ArrayList;
29  import java.util.Date;
30  import java.util.HashMap;
31  import java.util.List;
32  import java.util.Map;
33  
34  /**
35   * Parses the --porcelain format of git-blame
36   *
37   * For more information about the porcelain format, please read the official
38   * <a href="http://www.kernel.org/pub/software/scm/git/docs/git-blame.html#_the_porcelain_format">
39   * GIT blame porcelain format</a> description.
40   *
41   * @author Evgeny Mandrikov
42   * @author Olivier Lamy
43   * @author Mark Struberg
44   * @since 1.4
45   */
46  public class GitBlameConsumer
47      extends AbstractConsumer
48  {
49      private static final String GIT_COMMITTER_PREFIX = "committer";
50      private static final String GIT_COMMITTER      = GIT_COMMITTER_PREFIX + " ";
51      private static final String GIT_COMMITTER_TIME = GIT_COMMITTER_PREFIX + "-time ";
52      private static final String GIT_AUTHOR         = "author ";
53  
54  
55      private List<BlameLine> lines = new ArrayList<BlameLine>();
56  
57      /**
58       * Since the porcelain format only contains the commit information
59       * the first time a specific sha-1 commit appears, we need to store
60       * this information somwehere.
61       *
62       * key: the sha-1 of the commit
63       * value: the {@link BlameLine} containing the full committer/author info
64       */
65      private Map<String, BlameLine> commitInfo = new HashMap<String, BlameLine>();
66  
67      private boolean expectRevisionLine = true;
68  
69      private String revision  = null;
70      private String author    = null;
71      private String committer = null;
72      private Date   time      = null;
73  
74      public GitBlameConsumer( ScmLogger logger )
75      {
76          super( logger );
77      }
78  
79      public void consumeLine( String line )
80      {
81          if ( line == null )
82          {
83              return;
84          }
85  
86          if ( expectRevisionLine )
87          {
88              // this is the revision line
89              String parts[] = line.split( "\\s", 4 );
90  
91              if ( parts.length >= 1 )
92              {
93                  revision = parts[0];
94  
95                  BlameLine oldLine = commitInfo.get( revision );
96  
97                  if ( oldLine != null )
98                  {
99                      // restore the commit info
100                     author    = oldLine.getAuthor();
101                     committer = oldLine.getCommitter();
102                     time      = oldLine.getDate();
103                 }
104 
105                 expectRevisionLine = false;
106             }
107         }
108         else
109         {
110             if ( line.startsWith( GIT_AUTHOR ) )
111             {
112                 author = line.substring( GIT_AUTHOR.length() );
113                 return;
114             }
115 
116             if ( line.startsWith( GIT_COMMITTER ) )
117             {
118                 committer = line.substring( GIT_COMMITTER.length() );
119                 return;
120             }
121 
122             if ( line.startsWith( GIT_COMMITTER_TIME ) )
123             {
124                 String timeStr = line.substring( GIT_COMMITTER_TIME.length() );
125                 time = new Date( Long.parseLong( timeStr ) * 1000L );
126                 return;
127             }
128 
129 
130             if ( line.startsWith( "\t" ) )
131             {
132                 // this is the content line.
133                 // we actually don't need the content, but this is the right time to add the blame line
134                 BlameLine blameLine = new BlameLine( time, revision, author, committer );
135                 getLines().add( blameLine );
136 
137                 // keep commitinfo for this sha-1
138                 commitInfo.put( revision, blameLine );
139 
140                 if ( getLogger().isDebugEnabled() )
141                 {
142                     DateFormat df = SimpleDateFormat.getDateTimeInstance();
143                     getLogger().debug( author + " " + df.format( time ) );
144                 }
145 
146                 expectRevisionLine = true;
147             }
148 
149         }
150     }
151 
152     public List<BlameLine> getLines()
153     {
154         return lines;
155     }
156 }