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.info;
020
021import java.nio.file.Path;
022import java.time.format.DateTimeFormatter;
023
024import org.apache.commons.lang3.StringUtils;
025import org.apache.maven.scm.command.info.InfoItem;
026import org.apache.maven.scm.util.AbstractConsumer;
027import org.codehaus.plexus.util.cli.Arg;
028import org.codehaus.plexus.util.cli.Commandline;
029
030/**
031 * Parses output of {@code git log} with a particular format and populates a {@link InfoItem}.
032 *
033 * @author Olivier Lamy
034 * @since 1.5
035 * @see <a href="https://git-scm.com/docs/git-log#_pretty_formats">Pretty Formats</a>
036 */
037public class GitInfoConsumer extends AbstractConsumer {
038
039    private final InfoItem infoItem;
040    private final int revisionLength;
041
042    public GitInfoConsumer(Path path, int revisionLength) {
043        infoItem = new InfoItem();
044        infoItem.setPath(path.toString());
045        infoItem.setURL(path.toUri().toASCIIString());
046        this.revisionLength = revisionLength;
047    }
048
049    enum LineParts {
050        HASH(0),
051        AUTHOR_NAME(3),
052        AUTHOR_EMAIL(2),
053        AUTHOR_LAST_MODIFIED(1);
054
055        private final int index;
056
057        LineParts(int index) {
058            this.index = index;
059        }
060
061        public int getIndex() {
062            return index;
063        }
064    }
065
066    /**
067     * @param line the line which is supposed to have the format as specified by {@link #getFormatArgument()}.
068     * @see org.codehaus.plexus.util.cli.StreamConsumer#consumeLine(java.lang.String)
069     */
070    public void consumeLine(String line) {
071        if (logger.isDebugEnabled()) {
072            logger.debug("consume line {}", line);
073        }
074
075        // name must be last token as it may contain separators
076        String[] parts = line.split("\\s", 4);
077        if (parts.length != 4) {
078            throw new IllegalArgumentException(
079                    "Unexpected line: expecting 4 tokens separated by whitespace but got " + line);
080        }
081        infoItem.setLastChangedAuthor(
082                parts[LineParts.AUTHOR_NAME.getIndex()] + " <" + parts[LineParts.AUTHOR_EMAIL.getIndex()] + ">");
083        String revision = parts[LineParts.HASH.getIndex()];
084        if (revisionLength > -1) {
085            // do not truncate below 4 characters
086            revision = StringUtils.truncate(revision, Integer.max(4, revisionLength));
087        }
088        infoItem.setRevision(revision);
089        infoItem.setLastChangedDateTime(
090                DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(parts[LineParts.AUTHOR_LAST_MODIFIED.getIndex()]));
091    }
092
093    public InfoItem getInfoItem() {
094        return infoItem;
095    }
096
097    /**
098     * The format argument to use with {@code git log}
099     * @return the format argument to use {@code git log} command
100     * @see <a href="https://git-scm.com/docs/git-log#_pretty_formats">Pretty Formats</a>
101     */
102    public static Arg getFormatArgument() {
103        Commandline.Argument arg = new Commandline.Argument();
104        arg.setValue("--format=format:%H %aI %aE %aN");
105        return arg;
106    }
107}