001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.maven.scm.provider.git.gitexe.command.blame; 020 021import java.text.DateFormat; 022import java.text.SimpleDateFormat; 023import java.util.ArrayList; 024import java.util.Date; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Map; 028 029import org.apache.maven.scm.command.blame.BlameLine; 030import org.apache.maven.scm.util.AbstractConsumer; 031 032/** 033 * Parses the --porcelain format of git-blame 034 * 035 * For more information about the porcelain format, please read the official 036 * <a href="http://www.kernel.org/pub/software/scm/git/docs/git-blame.html#_the_porcelain_format"> 037 * GIT blame porcelain format</a> description. 038 * 039 * @author Evgeny Mandrikov 040 * @author Olivier Lamy 041 * @author Mark Struberg 042 * @since 1.4 043 */ 044public class GitBlameConsumer extends AbstractConsumer { 045 private static final String GIT_COMMITTER_PREFIX = "committer"; 046 private static final String GIT_COMMITTER = GIT_COMMITTER_PREFIX + " "; 047 private static final String GIT_COMMITTER_TIME = GIT_COMMITTER_PREFIX + "-time "; 048 private static final String GIT_AUTHOR = "author "; 049 050 private final List<BlameLine> lines = new ArrayList<>(); 051 052 /** 053 * Since the porcelain format only contains the commit information 054 * the first time a specific sha-1 commit appears, we need to store 055 * this information somwehere. 056 * 057 * key: the sha-1 of the commit 058 * value: the {@link BlameLine} containing the full committer/author info 059 */ 060 private final Map<String, BlameLine> commitInfo = new HashMap<>(); 061 062 private boolean expectRevisionLine = true; 063 064 private String revision = null; 065 private String author = null; 066 private String committer = null; 067 private Date time = null; 068 069 public void consumeLine(String line) { 070 if (line == null) { 071 return; 072 } 073 074 if (expectRevisionLine) { 075 // this is the revision line 076 String parts[] = line.split("\\s", 4); 077 078 if (parts.length >= 1) { 079 revision = parts[0]; 080 081 BlameLine oldLine = commitInfo.get(revision); 082 083 if (oldLine != null) { 084 // restore the commit info 085 author = oldLine.getAuthor(); 086 committer = oldLine.getCommitter(); 087 time = oldLine.getDate(); 088 } 089 090 expectRevisionLine = false; 091 } 092 } else { 093 if (line.startsWith(GIT_AUTHOR)) { 094 author = line.substring(GIT_AUTHOR.length()); 095 return; 096 } 097 098 if (line.startsWith(GIT_COMMITTER)) { 099 committer = line.substring(GIT_COMMITTER.length()); 100 return; 101 } 102 103 if (line.startsWith(GIT_COMMITTER_TIME)) { 104 String timeStr = line.substring(GIT_COMMITTER_TIME.length()); 105 time = new Date(Long.parseLong(timeStr) * 1000L); 106 return; 107 } 108 109 if (line.startsWith("\t")) { 110 // this is the content line. 111 // we actually don't need the content, but this is the right time to add the blame line 112 BlameLine blameLine = new BlameLine(time, revision, author, committer); 113 getLines().add(blameLine); 114 115 // keep commitinfo for this sha-1 116 commitInfo.put(revision, blameLine); 117 118 if (logger.isDebugEnabled()) { 119 DateFormat df = SimpleDateFormat.getDateTimeInstance(); 120 logger.debug(author + " " + df.format(time)); 121 } 122 123 expectRevisionLine = true; 124 } 125 } 126 } 127 128 public List<BlameLine> getLines() { 129 return lines; 130 } 131}