All Downloads are FREE. Search and download functionalities are using the official Maven repository.

team.yi.tools.semanticcommit.parser.CommitParser Maven / Gradle / Ivy

package team.yi.tools.semanticcommit.parser;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import team.yi.tools.semanticcommit.CommitUtils;
import team.yi.tools.semanticcommit.model.GitCommit;
import team.yi.tools.semanticcommit.model.IssueRef;
import team.yi.tools.semanticcommit.model.MentionRef;
import team.yi.tools.semanticcommit.model.ReleaseCommit;
import team.yi.tools.semanticcommit.model.ReleaseCommitLocale;
import team.yi.tools.semanticcommit.parser.lexer.CommitLexer;
import team.yi.tools.semanticcommit.parser.lexer.LexerConstants;
import team.yi.tools.semanticcommit.parser.lexer.Token;
import team.yi.tools.semanticcommit.parser.lexer.TokenKind;

import java.io.IOException;
import java.util.List;
import java.util.Locale;

@SuppressWarnings("PMD.TooManyMethods")
@Slf4j
public class CommitParser extends Parser {
    private final CommitParserSettings settings;
    private final GitCommit gitCommit;
    private final List closeIssueActions;
    private ReleaseCommit releaseCommit;

    public CommitParser(final CommitParserSettings settings, final GitCommit gitCommit) throws IOException {
        super(new CommitLexer(gitCommit.getMessage(), settings.getCloseIssueActions()));

        this.settings = settings;
        this.gitCommit = gitCommit;
        this.closeIssueActions = settings.getCloseIssueActions();
    }

    @Override
    public void reset() {
        super.reset();

        this.releaseCommit = new ReleaseCommit(this.gitCommit, this.settings.getDefaultLang());

        final String commitUrl = CommitUtils.createCommitUrl(this.settings.getCommitUrlTemplate(), this.releaseCommit.getHashFull());

        this.releaseCommit.setCommitUrl(commitUrl);
    }

    @Override
    public ReleaseCommit parse() {
        this.reset();
        this.consume();

        while (this.current.getKind() != TokenKind.eof) {
            switch (this.current.getKind()) {
                case type:
                    this.releaseCommit.setCommitType(this.current.getValue());
                    break;
                case attention:
                    this.releaseCommit.setAttention("!".equals(this.current.getValue()));
                    break;
                case scope:
                    this.readScope();
                    break;
                case subject:
                    this.readSubject();
                    break;
                case body:
                    this.readBody();
                    break;
                case sectionBoundary:
                    this.readSection();
                    break;
                default:
                    break;
            }

            this.consume();
        }

        return this.releaseCommit;
    }

    private IssueRef readIssue(final boolean forSubject) {
        this.consume(TokenKind.issueStart);
        this.consume(TokenKind.text);

        final Integer issueId = Integer.valueOf(this.current.getValue());
        final String url = CommitUtils.createIssueUrl(this.settings.getIssueUrlTemplate(), issueId);
        final IssueRef issueRef = new IssueRef(issueId, url);

        if (forSubject) {
            this.releaseCommit.getSubjectIssues().add(issueRef);
        } else {
            this.releaseCommit.getBodyIssues().add(issueRef);
        }

        return issueRef;
    }

    private IssueRef readIssueAction() {
        final String action = StringUtils.stripStart(this.current.getValue(), "/");

        this.consume(TokenKind.issueAction);

        final String repo = StringUtils.stripToNull(this.current.getValue());

        this.consume(TokenKind.issueRepo);
        this.consume(TokenKind.issueStart);
        this.consume(TokenKind.text);

        final Integer issueId = Integer.valueOf(this.current.getValue());
        final String url = CommitUtils.createIssueUrl(this.settings.getIssueUrlTemplate(), issueId);
        final IssueRef issueRef = new IssueRef(repo, issueId, url, action);

        this.releaseCommit.add(action, issueRef);

        if (this.closeIssueActions.contains(action.toLowerCase(Locale.getDefault()))) {
            releaseCommit.getCloseIssues().add(issueRef);
        }

        return issueRef;
    }

    private MentionRef readMentionRef() {
        this.consume(TokenKind.mentionStart);

        final String username = this.current.getValue();
        final String url = CommitUtils.createMentionUrl(this.settings.getMentionUrlTemplate(), username);
        final MentionRef mentionRef = new MentionRef(username, url);

        this.releaseCommit.getMentions().add(mentionRef);

        this.consume(TokenKind.mention);

        return mentionRef;
    }

    private void readLocales() {
        this.consume();

        if (this.current.getKind() != TokenKind.localeItemStart) return;

        String lang = null;
        String subject = null;

        while (this.current.getKind() != TokenKind.sectionBoundary && this.current.getKind() != TokenKind.eof) {
            this.consume();

            switch (this.current.getKind()) {
                case localeLang:
                    lang = this.current.getValue();
                    break;
                case localeSubject:
                    subject = this.current.getValue();
                    break;
                case localeItemEnd:
                case eof:
                    if (StringUtils.isEmpty(lang)) break;

                    final ReleaseCommitLocale locale = new ReleaseCommitLocale(this.gitCommit.getHashFull(), lang, subject);

                    this.releaseCommit.getLocales().add(locale);

                    lang = StringUtils.EMPTY;
                    subject = StringUtils.EMPTY;
                    break;
                default:
                    break;
            }
        }
    }

    private void readSection() {
        this.consume();

        if (this.current.getKind() == TokenKind.localeListHeader) {
            this.readLocales();
        }
    }

    private void readBody() {
        final StringBuilder builder = new StringBuilder("\n\n");

        while (this.current.getKind() != TokenKind.bodyEnd && this.current.getKind() != TokenKind.eof) {
            switch (this.current.getKind()) {
                case issueEnd:
                case body:
                    builder.append(this.current.getValue());
                    break;
                case mentionStart:
                    final MentionRef mentionRef = this.readMentionRef();

                    builder.append(LexerConstants.AT).append(mentionRef.getUsername());
                    break;
                case issueAction: {
                    final IssueRef issueRef = this.readIssueAction();

                    builder.append(issueRef.getAction()).append(LexerConstants.SPACE);

                    if (StringUtils.isNotEmpty(issueRef.getRepo())) {
                        builder.append(issueRef.getRepo()).append(LexerConstants.SLASH);
                    }

                    builder.append(LexerConstants.SHARP).append(issueRef.getId());
                    break;
                }
                case issueStart: {
                    final String issueStart = StringUtils.prependIfMissing(this.current.getValue(), StringUtils.SPACE);
                    final IssueRef issueRef = this.readIssue(false);

                    builder.append(issueStart)
                        .append(LexerConstants.SHARP)
                        .append(issueRef.getId());
                    break;
                }
                // case mentionEnd:
                // case bodyEnd:
                default:
                    break;
            }

            this.consume();
        }

        this.releaseCommit.setCommitBody(builder.toString().trim());

        final String[] lines = StringUtils.split(this.releaseCommit.getCommitBody(), "\r\n");

        for (final String line : lines) {
            if (!this.releaseCommit.isBreakingChange() && StringUtils.startsWith(line, ParserConstants.BREAKING_CHANGE_PATTERN)) {
                this.releaseCommit.setBreakingChange(true);
            }

            if (!this.releaseCommit.isDeprecated() && StringUtils.startsWith(line, ParserConstants.DEPRECATED_PATTERN)) {
                this.releaseCommit.setDeprecated(true);
            }
        }
    }

    private void readSubject() {
        final StringBuilder builder = new StringBuilder();

        while (this.current.getKind() != TokenKind.subjectEnd && this.current.getKind() != TokenKind.eof) {
            switch (this.current.getKind()) {
                case issueEnd:
                case subject:
                    builder.append(this.current.getValue());
                    break;
                case mentionStart:
                    final MentionRef mentionRef = this.readMentionRef();

                    builder.append(LexerConstants.AT).append(mentionRef.getUsername());
                    break;
                case issueStart:
                    final String issueStart = StringUtils.prependIfMissing(this.current.getValue(), StringUtils.SPACE);
                    final IssueRef issueRef = this.readIssue(true);

                    builder.append(issueStart).append(LexerConstants.SHARP).append(issueRef.getId());
                    break;
                // case mentionEnd:
                case subjectEnd:
                default:
                    break;
            }

            this.consume();
        }

        this.releaseCommit.setCommitSubject(builder.toString());
    }

    private void readScope() {
        final Token token = this.consume(TokenKind.scope);
        final int pos = token.getValue().lastIndexOf(LexerConstants.SLASH);

        if (pos > -1) {
            final String packageName = token.getValue().substring(0, pos + 1);
            final String scope = token.getValue().substring(pos + 1);

            this.releaseCommit.setCommitPackage(packageName);
            this.releaseCommit.setCommitScope(scope);
        } else {
            this.releaseCommit.setCommitScope(token.getValue());
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy