public static final String DEFAULT_DATEFORMAT = "yyyy-MM-dd HH:mm:ss"; public static final String DEFAULT_GITHUB_ISSUE_PATTERN = "#([0-9]+)"; public static final String DEFAULT_GITLAB_ISSUE_PATTERN = "#([0-9]+)"; public static final String DEFAULT_IGNORE_COMMITS_REGEXP = ""; public static final String DEFAULT_JIRA_ISSUE_PATTEN = "\\b[a-zA-Z]([a-zA-Z]+)-([0-9]+)\\b"; public static final String DEFAULT_MINOR_PATTERN = "^[Ff]eat.*"; public static final String DEFAULT_NO_ISSUE_NAME = "No issue"; public static final String DEFAULT_PATCH_PATTERN = null; public static final String DEFAULT_READABLE_TAG_NAME = "/([^/]+?)$"; public static final String DEFAULT_REDMINE_ISSUE_PATTEN = "#([0-9]+)"; public static final boolean DEFAULT_REMOVE_ISSUE = false; public static final String DEFAULT_TIMEZONE = "UTC"; public static final String DEFAULT_UNTAGGED_NAME = "Unreleased"; public static final String ZERO_COMMIT = "0000000000000000000000000000000000000000"; public static String emptyToNull(final String from) {
  if (from != null && !from.trim().isEmpty()) {
    return from;
  }
  return null;
} public class Changelog implements ICommits, IAuthors, IIssues, Serializable {
  private static final long serialVersionUID = 2193789018496738737L;
  private final List<Commit> commits;
  private final List<Tag> tags;
  private final List<Author> authors;
  private final List<Issue> issues;
  private final List<IssueType> issueTypes;
  private final String ownerName;
  private final String repoName;

  public Changelog(
      final List<Commit> commits,
      final List<Tag> tags,
      final List<Author> authors,
      final List<Issue> issues,
      final List<IssueType> issueTypes,
      final String ownerName,
      final String repoName) {
    this.commits = checkNotNull(commits, "commits");
    this.tags = checkNotNull(tags, "tags");
    this.authors = checkNotNull(authors, "authors");
    this.issues = checkNotNull(issues, "issues");
    this.issueTypes = checkNotNull(issueTypes, "issueTypes");
    this.ownerName = ownerName;
    this.repoName = repoName;
  }

  @Override
  public List<Issue> getIssues() {
    return this.issues;
  }

  @Override
  public List<Author> getAuthors() {
    return this.authors;
  }

  @Override
  public List<Commit> getCommits() {
    return this.commits;
  }

  public String getOwnerName() {
    return this.ownerName;
  }

  public String getRepoName() {
    return this.repoName;
  }

  public List<Tag> getTags() {
    return this.tags;
  }

  public List<IssueType> getIssueTypes() {
    return this.issueTypes;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((this.authors == null) ? 0 : this.authors.hashCode());
    result = prime * result + ((this.commits == null) ? 0 : this.commits.hashCode());
    result = prime * result + ((this.issueTypes == null) ? 0 : this.issueTypes.hashCode());
    result = prime * result + ((this.issues == null) ? 0 : this.issues.hashCode());
    result = prime * result + ((this.ownerName == null) ? 0 : this.ownerName.hashCode());
    result = prime * result + ((this.repoName == null) ? 0 : this.repoName.hashCode());
    result = prime * result + ((this.tags == null) ? 0 : this.tags.hashCode());
    return result;
  }

  @Override
  public boolean equals(final Object obj) {
    if (this == obj) {
      return true;
    }
    if (obj == null) {
      return false;
    }
    if (this.getClass() != obj.getClass()) {
      return false;
    }
    final Changelog other = (Changelog) obj;
    if (this.authors == null) {
      if (other.authors != null) {
        return false;
      }
    } else if (!this.authors.equals(other.authors)) {
      return false;
    }
    if (this.commits == null) {
      if (other.commits != null) {
        return false;
      }
    } else if (!this.commits.equals(other.commits)) {
      return false;
    }
    if (this.issueTypes == null) {
      if (other.issueTypes != null) {
        return false;
      }
    } else if (!this.issueTypes.equals(other.issueTypes)) {
      return false;
    }
    if (this.issues == null) {
      if (other.issues != null) {
        return false;
      }
    } else if (!this.issues.equals(other.issues)) {
      return false;
    }
    if (this.ownerName == null) {
      if (other.ownerName != null) {
        return false;
      }
    } else if (!this.ownerName.equals(other.ownerName)) {
      return false;
    }
    if (this.repoName == null) {
      if (other.repoName != null) {
        return false;
      }
    } else if (!this.repoName.equals(other.repoName)) {
      return false;
    }
    if (this.tags == null) {
      if (other.tags != null) {
        return false;
      }
    } else if (!this.tags.equals(other.tags)) {
      return false;
    }
    return true;
  }
} public class Issue implements ICommits, IAuthors, Serializable {

  private static final long serialVersionUID = -7571341639024417199L;
  private final List<Commit> commits;
  private final List<Author> authors;
  /** Like JIRA, or GitHub. */
  private final String name;

  /** Like the title of a Jira. */
  private final String title;

  private final boolean hasTitle;

  /** Like the actual Jira, JIR-ABC. */
  private final String issue;

  private final boolean hasIssue;

  /** A link to the issue, http://..... */
  private final String link;

  private final boolean hasLink;

  /** Type of issue, perhaps Story, Bug and etc */
  private final String type;

  private final boolean hasType;

  private final boolean hasDescription;
  private final String description;
  /** Labels on the issue, for GitHub it may be bug, enhancement, ... */
  private final List<String> labels;

  private final boolean hasLabels;
  private final boolean hasLinkedIssues;
  private final SettingsIssueType issueType;
  private final List<String> linkedIssues;

  public Issue(
      final List<Commit> commits,
      final List<Author> authors,
      final String name,
      final String title,
      final String issue,
      final SettingsIssueType issueType,
      final String description,
      final String link,
      final String type,
      final List<String> linkedIssues,
      final List<String> labels) {
    checkState(!commits.isEmpty(), "commits");
    this.commits = commits;
    this.authors = checkNotNull(authors, "authors");
    this.name = checkNotNull(name, "name");
    this.title = nullToEmpty(title);
    this.hasTitle = !isNullOrEmpty(title);
    this.description = nullToEmpty(description);
    this.hasDescription = !isNullOrEmpty(description);
    this.issue = nullToEmpty(issue);
    this.issueType = checkNotNull(issueType, "issueType");
    this.hasIssue = !isNullOrEmpty(issue);
    this.link = nullToEmpty(link);
    this.hasLink = !isNullOrEmpty(link);
    this.hasType = !isNullOrEmpty(type);
    this.type = nullToEmpty(type);
    this.hasLabels = labels != null && !labels.isEmpty();
    this.hasLinkedIssues = linkedIssues != null && !linkedIssues.isEmpty();
    this.linkedIssues = linkedIssues;
    this.labels = labels;
  }

  public SettingsIssueType getIssueType() {
    return this.issueType;
  }

  public boolean isJira() {
    return this.issueType == JIRA;
  }

  public boolean isRedmine() {
    return this.issueType == REDMINE;
  }

  public boolean isGitHub() {
    return this.issueType == GITHUB;
  }

  public boolean isGitLab() {
    return this.issueType == GITLAB;
  }

  public boolean isCustom() {
    return this.issueType == CUSTOM;
  }

  public boolean isNoIssue() {
    return this.issueType == NOISSUE;
  }

  public String getTitle() {
    return this.title;
  }

  public boolean getHasTitle() {
    return this.hasTitle;
  }

  public boolean getHasIssue() {
    return this.hasIssue;
  }

  public boolean getHasLabels() {
    return this.hasLabels;
  }

  public boolean getHasType() {
    return this.hasType;
  }

  public boolean getHasLink() {
    return this.hasLink;
  }

  public String getIssue() {
    return this.issue;
  }

  public String getLink() {
    return this.link;
  }

  public String getName() {
    return this.name;
  }

  public String getType() {
    return this.type;
  }

  public boolean getHasDescription() {
    return this.hasDescription;
  }

  public String getDescription() {
    return this.description;
  }

  @Override
  public List<Author> getAuthors() {
    return this.authors;
  }

  @Override
  public List<Commit> getCommits() {
    return this.commits;
  }

  public List<String> getLabels() {
    return this.labels;
  }

  public List<String> getLinkedIssues() {
    return this.linkedIssues;
  }

  public boolean getHasLinkedIssues() {
    return this.hasLinkedIssues;
  }

  @Override
  public String toString() {
    return "Issue: " + this.issue + " Title: " + this.title;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((this.authors == null) ? 0 : this.authors.hashCode());
    result = prime * result + ((this.commits == null) ? 0 : this.commits.hashCode());
    result = prime * result + ((this.description == null) ? 0 : this.description.hashCode());
    result = prime * result + (this.hasDescription ? 1231 : 1237);
    result = prime * result + (this.hasIssue ? 1231 : 1237);
    result = prime * result + (this.hasLabels ? 1231 : 1237);
    result = prime * result + (this.hasLink ? 1231 : 1237);
    result = prime * result + (this.hasLinkedIssues ? 1231 : 1237);
    result = prime * result + (this.hasTitle ? 1231 : 1237);
    result = prime * result + (this.hasType ? 1231 : 1237);
    result = prime * result + ((this.issue == null) ? 0 : this.issue.hashCode());
    result = prime * result + ((this.issueType == null) ? 0 : this.issueType.hashCode());
    result = prime * result + ((this.labels == null) ? 0 : this.labels.hashCode());
    result = prime * result + ((this.link == null) ? 0 : this.link.hashCode());
    result = prime * result + ((this.linkedIssues == null) ? 0 : this.linkedIssues.hashCode());
    result = prime * result + ((this.name == null) ? 0 : this.name.hashCode());
    result = prime * result + ((this.title == null) ? 0 : this.title.hashCode());
    result = prime * result + ((this.type == null) ? 0 : this.type.hashCode());
    return result;
  }

  @Override
  public boolean equals(final Object obj) {
    if (this == obj) {
      return true;
    }
    if (obj == null) {
      return false;
    }
    if (this.getClass() != obj.getClass()) {
      return false;
    }
    final Issue other = (Issue) obj;
    if (this.authors == null) {
      if (other.authors != null) {
        return false;
      }
    } else if (!this.authors.equals(other.authors)) {
      return false;
    }
    if (this.commits == null) {
      if (other.commits != null) {
        return false;
      }
    } else if (!this.commits.equals(other.commits)) {
      return false;
    }
    if (this.description == null) {
      if (other.description != null) {
        return false;
      }
    } else if (!this.description.equals(other.description)) {
      return false;
    }
    if (this.hasDescription != other.hasDescription) {
      return false;
    }
    if (this.hasIssue != other.hasIssue) {
      return false;
    }
    if (this.hasLabels != other.hasLabels) {
      return false;
    }
    if (this.hasLink != other.hasLink) {
      return false;
    }
    if (this.hasLinkedIssues != other.hasLinkedIssues) {
      return false;
    }
    if (this.hasTitle != other.hasTitle) {
      return false;
    }
    if (this.hasType != other.hasType) {
      return false;
    }
    if (this.issue == null) {
      if (other.issue != null) {
        return false;
      }
    } else if (!this.issue.equals(other.issue)) {
      return false;
    }
    if (this.issueType != other.issueType) {
      return false;
    }
    if (this.labels == null) {
      if (other.labels != null) {
        return false;
      }
    } else if (!this.labels.equals(other.labels)) {
      return false;
    }
    if (this.link == null) {
      if (other.link != null) {
        return false;
      }
    } else if (!this.link.equals(other.link)) {
      return false;
    }
    if (this.linkedIssues == null) {
      if (other.linkedIssues != null) {
        return false;
      }
    } else if (!this.linkedIssues.equals(other.linkedIssues)) {
      return false;
    }
    if (this.name == null) {
      if (other.name != null) {
        return false;
      }
    } else if (!this.name.equals(other.name)) {
      return false;
    }
    if (this.title == null) {
      if (other.title != null) {
        return false;
      }
    } else if (!this.title.equals(other.title)) {
      return false;
    }
    if (this.type == null) {
      if (other.type != null) {
        return false;
      }
    } else if (!this.type.equals(other.type)) {
      return false;
    }
    return true;
  }
}
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Optional.ofNullable;
import static se.bjurr.gitchangelog.api.GitChangelogApiConstants.DEFAULT_DATEFORMAT;
import static se.bjurr.gitchangelog.api.GitChangelogApiConstants.DEFAULT_GITHUB_ISSUE_PATTERN;
import static se.bjurr.gitchangelog.api.GitChangelogApiConstants.DEFAULT_GITLAB_ISSUE_PATTERN;
import static se.bjurr.gitchangelog.api.GitChangelogApiConstants.DEFAULT_IGNORE_COMMITS_REGEXP;
import static se.bjurr.gitchangelog.api.GitChangelogApiConstants.DEFAULT_JIRA_ISSUE_PATTEN;
import static se.bjurr.gitchangelog.api.GitChangelogApiConstants.DEFAULT_MINOR_PATTERN;
import static se.bjurr.gitchangelog.api.GitChangelogApiConstants.DEFAULT_NO_ISSUE_NAME;
import static se.bjurr.gitchangelog.api.GitChangelogApiConstants.DEFAULT_PATCH_PATTERN;
import static se.bjurr.gitchangelog.api.GitChangelogApiConstants.DEFAULT_READABLE_TAG_NAME;
import static se.bjurr.gitchangelog.api.GitChangelogApiConstants.DEFAULT_REDMINE_ISSUE_PATTEN;
import static se.bjurr.gitchangelog.api.GitChangelogApiConstants.DEFAULT_REMOVE_ISSUE;
import static se.bjurr.gitchangelog.api.GitChangelogApiConstants.DEFAULT_TIMEZONE;
import static se.bjurr.gitchangelog.api.GitChangelogApiConstants.DEFAULT_UNTAGGED_NAME;
import static se.bjurr.gitchangelog.api.GitChangelogApiConstants.ZERO_COMMIT;
import static se.bjurr.gitchangelog.internal.util.Preconditions.emptyToNull;
import com.google.gson.Gson;
import java.io.Serializable;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import se.bjurr.gitchangelog.api.model.Changelog;
import se.bjurr.gitchangelog.api.model.Issue;
package se.bjurr.gitchangelog.internal.settings;



public class Settings implements Serializable {
  private static final long serialVersionUID = 4565886594381385244L;

  private static Gson gson = new Gson();

  /** Folder where repo lives. */
  private String fromRepo;
  /** Include all commits from here. Any tag or branch name. */
  private String fromRef;
  /**
   * Include all commits to this reference. Any tag or branch name. There is a constant for master
   * here: reference{GitChangelogApiConstants#REF_MASTER}.
   */
  private String toRef;
  /**
   * Include all commits from here. Any commit hash. There is a constant pointing at the first
   * commit here: reference{GitChangelogApiConstants#ZERO_COMMIT}.
   */
  private String fromCommit;
  /** Include all commits to here. Any commit hash. */
  private String toCommit;
  /**
   * A regular expression that is evaluated on each tag. If it matches, the tag will be filtered out
   * and not included in the changelog.
   */
  private String ignoreTagsIfNameMatches;
  /**
   * A regular expression that is evaluated on the commit message of each commit. If it matches, the
   * commit will be filtered out and not included in the changelog.<br>
   * <br>
   * To ignore tags creted by Maven and Gradle release plugins, perhaps you want this: <br>
   * <code>
   * ^\[maven-release-plugin\].*|^\[Gradle Release Plugin\].*|^Merge.*
   * </code><br>
   * <br>
   * Remember to escape it, if added to the json-file it would look like this:<br>
   * <code>
   * ^\\[maven-release-plugin\\].*|^\\[Gradle Release Plugin\\].*|^Merge.*
   * </code>
   */
  private String ignoreCommitsIfMessageMatches;
  /**
   * A date that is evaluated on the commit time of each commit. If the commit is older than the
   * point in time given, then it will be filtered out and not included in the changelog. <br>
   * See {@link SimpleDateFormat}.
   */
  private Date ignoreCommitsIfOlderThan;
  /**
   * Some commits may not be included in any tag. Commits that not released yet may not be tagged.
   * This is a "virtual tag", added to {@link Changelog#getTags()}, that includes those commits. A
   * fitting value may be "Next release".
   */
  private String untaggedName;
  /**
   * Path of template-file to use. It is a Mustache (https://mustache.github.io/) template. Supplied
   * with the context of {@link Changelog}.
   */
  private String templatePath;
  /**
   * Path to the base directory for template partial files. If not null, handlebars will be
   * configured with a FileTemplateLoader with this as base directory.
   */
  private String templateBaseDir;
  /** The filename suffix of template partial files. Requires "templateBaseDir" to be set. */
  private String templateSuffix;
  /**
   * Your tags may look something like <code>git-changelog-maven-plugin-1.6</code>. But in the
   * changelog you just want <code>1.6</code>. With this regular expression, the numbering can be
   * extracted from the tag name.<br>
   * <code>/([^-]+?)$</code>
   */
  private String readableTagName;
  /** Format of dates, see {@link SimpleDateFormat}. */
  private String dateFormat;
  /**
   * This is a "virtual issue" that is added to {@link Changelog#getIssues()}. It contains all
   * commits that has no issue in the commit comment. This could be used as a "wall of shame"
   * listing commiters that did not tag there commits with an issue.
   */
  private String noIssueName;
  /**
   * When date of commits are translated to a string, this timezone is used.<br>
   * <code>UTC</code>
   */
  private String timeZone;
  /**
   * If true, the changelog will not contain the issue in the commit comment. If your changelog is
   * grouped by issues, you may want this to be true. If not grouped by issue, perhaps false.
   */
  private boolean removeIssueFromMessage;
  /** Use any configured feature with Jira. */
  private boolean jiraEnabled;
  /**
   * URL pointing at your JIRA server. When configured, the {@link Issue#getTitle()} will be
   * populated with title from JIRA.<br>
   * <code>https://jiraserver/jira</code>
   */
  private String jiraServer;
  /**
   * Pattern to recognize JIRA:s. <code>\b[a-zA-Z]([a-zA-Z]+)-([0-9]+)\b</code><br>
   * <br>
   * Or escaped if added to json-file:<br>
   * <code>\\b[a-zA-Z]([a-zA-Z]+)-([0-9]+)\\b</code>
   */
  private String jiraIssuePattern;
  /** Authenticate to JIRA. */
  private String jiraUsername;
  /** Authenticate to JIRA. */
  private String jiraPassword;
  /** Authenticate to JIRA. */
  private String jiraToken;
  /** Authenticate to JIRA. */
  private String jiraBearer;

  /** Use any configured feature with Redmine. */
  private boolean redmineEnabled;
  /**
   * URL pointing at your Redmine server. When configured, the {@link Issue#getTitle()} will be
   * populated with title from Redmine.<br>
   * <code>https://redmine/redmine</code>
   */
  private String redmineServer;
  /** Pattern to recognize Redmine:s. <code>#([0-9]+)</code> */
  private String redmineIssuePattern;
  /** Authenticate to Redmine. */
  private String redmineUsername;
  /** Authenticate to Redmine. */
  private String redminePassword;
  /** Authenticate to Redmine whith API_KEY */
  private String redmineToken;
  /** Use any configured feature with Github. */
  private boolean gitHubEnabled;
  /**
   * URL pointing at GitHub API. When configured, the {@link Issue#getTitle()} will be populated
   * with title from GitHub.<br>
   * <code>https://api.github.com/repos/tomasbjerre/git-changelog-lib</code>
   */
  private String gitHubApi;
  /**
   * GitHub authentication token. Configure to avoid low rate limits imposed by GitHub in case you
   * have a lot of issues and/or pull requests.<br>
   * <code>https://api.github.com/repos/tomasbjerre/git-changelog-lib</code>
   */
  private String gitHubToken;
  /** Pattern to recognize GitHub:s. <code>#([0-9]+)</code> */
  private String gitHubIssuePattern;
  /**
   * Custom issues are added to support any kind of issue management, perhaps something that is
   * internal to your project. See {@link SettingsIssue}.
   */
  private List<SettingsIssue> customIssues;
  /**
   * Extended variables is simply a key-value mapping of variables that are made available in the
   * template. Is used, for example, by the Bitbucket plugin to supply some internal variables to
   * the changelog context.
   */
  private Map<String, Object> extendedVariables = new HashMap<>();

  /**
   * Extended headers is simply a key-value mapping of headers that will be passed to REST request.
   * Is used, for example, to bypass 2-factor authentication.
   */
  private Map<String, String> extendedRestHeaders;

  /** Commits that don't have any issue in their commit message will not be included. */
  private boolean ignoreCommitsWithoutIssue;

  /** Use any configured feature with Gitlab. */
  private boolean gitLabEnabled;
  /** GitLab server URL, like https://gitlab.com/. */
  private String gitLabServer;

  private String gitLabToken;

  /** Pattern to recognize GitLab:s. <code>#([0-9]+)</code> */
  private String gitLabIssuePattern;

  /**
   * Like: tomas.bjerre85/violations-test for this repo:
   * https://gitlab.com/tomas.bjerre85/violations-test
   */
  private String gitLabProjectName;

  /** Regular expression to use when determining next semantic version based on commits. */
  private String semanticMajorPattern = null;

  /** Regular expression to use when determining next semantic version based on commits. */
  private String semanticMinorPattern = null;

  /** Regular expression to use when determining next semantic version based on commits. */
  private String semanticPatchPattern;

  /** Integrate with services to get more details about issues. */
  private boolean useIntegrations;

  public String getSubDirFilter() {
    return ofNullable(this.subDirFilter).orElse("");
  }

  public void setPathFilter(final String subDirFilter) {
    this.subDirFilter = subDirFilter;
  }

  private String subDirFilter;

  public Settings() {}

  public void setCustomIssues(final List<SettingsIssue> customIssues) {
    this.customIssues = customIssues;
  }

  public void setFromRef(final String fromRef) {
    if (fromRef == null || fromRef.trim().isEmpty()) {
      this.fromRef = null;
    } else {
      this.fromRef = fromRef.trim();
    }
  }

  public void setToRef(final String toRef) {
    if (toRef == null || toRef.trim().isEmpty()) {
      this.toRef = null;
    } else {
      this.toRef = toRef.trim();
    }
  }

  public Optional<String> getFromRef() {
    return ofNullable(this.fromRef);
  }

  public Optional<String> getToRef() {
    return ofNullable(this.toRef);
  }

  public void setFromRepo(final String fromRepo) {
    this.fromRepo = fromRepo;
  }

  public String getFromRepo() {
    return ofNullable(this.fromRepo).orElse(".");
  }

  public void setIgnoreCommitsIfMessageMatches(final String ignoreCommitsIfMessageMatches) {
    this.ignoreCommitsIfMessageMatches = ignoreCommitsIfMessageMatches;
  }

  public void setIgnoreTagsIfNameMatches(final String ignoreTagsIfNameMatches) {
    this.ignoreTagsIfNameMatches = ignoreTagsIfNameMatches;
  }

  public void setIgnoreCommitsIfOlderThan(final Date ignoreCommitsIfOlderThan) {
    if (ignoreCommitsIfOlderThan != null) {
      this.ignoreCommitsIfOlderThan = new Date(ignoreCommitsIfOlderThan.getTime());
    } else {
      this.ignoreCommitsIfOlderThan = null;
    }
  }

  public void setJiraIssuePattern(final String jiraIssuePattern) {
    this.jiraIssuePattern = jiraIssuePattern;
  }

  public void setJiraServer(final String jiraServer) {
    this.jiraServer = jiraServer;
  }

  public void setRedmineIssuePattern(final String redmineIssuePattern) {
    this.redmineIssuePattern = redmineIssuePattern;
  }

  public void setRedmineServer(final String redmineServer) {
    this.redmineServer = redmineServer;
  }

  public void addCustomIssue(final SettingsIssue customIssue) {
    if (this.customIssues == null) {
      this.customIssues = new ArrayList<>();
    }
    this.customIssues.add(customIssue);
  }

  public List<SettingsIssue> getCustomIssues() {
    if (this.customIssues == null) {
      return new ArrayList<>();
    } else {
      return this.customIssues;
    }
  }

  public String getIgnoreCommitsIfMessageMatches() {
    return ofNullable(this.ignoreCommitsIfMessageMatches).orElse(DEFAULT_IGNORE_COMMITS_REGEXP);
  }

  public Optional<Date> getIgnoreCommitsIfOlderThan() {
    return ofNullable(this.ignoreCommitsIfOlderThan);
  }

  public String getJiraIssuePattern() {
    return ofNullable(this.jiraIssuePattern).orElse(DEFAULT_JIRA_ISSUE_PATTEN);
  }

  public Optional<String> getJiraServer() {
    return ofNullable(this.jiraServer);
  }

  public String getRedmineIssuePattern() {
    return ofNullable(this.redmineIssuePattern).orElse(DEFAULT_REDMINE_ISSUE_PATTEN);
  }

  public Optional<String> getRedmineServer() {
    return ofNullable(this.redmineServer);
  }

  public static Settings fromFile(final URL url) {
    try {
      return gson.fromJson(
          new String(Files.readAllBytes(Paths.get(url.toURI())), UTF_8), Settings.class);
    } catch (final Exception e) {
      throw new RuntimeException("Cannot read " + url, e);
    }
  }

  public void setFromCommit(final String fromCommit) {
    this.fromCommit = fromCommit;
  }

  public void setToCommit(final String toCommit) {
    this.toCommit = toCommit;
  }

  public Optional<String> getFromCommit() {
    return ofNullable(emptyToNull(this.fromCommit));
  }

  public Optional<String> getToCommit() {
    return ofNullable(emptyToNull(this.toCommit));
  }

  public String getUntaggedName() {
    return ofNullable(this.untaggedName).orElse(DEFAULT_UNTAGGED_NAME);
  }

  public Optional<String> getIgnoreTagsIfNameMatches() {
    return ofNullable(this.ignoreTagsIfNameMatches);
  }

  public void setUntaggedName(final String untaggedName) {
    this.untaggedName = untaggedName;
  }

  public String getTemplatePath() {
    return ofNullable(this.templatePath).orElse("changelog.mustache");
  }

  public void setTemplatePath(final String templatePath) {
    this.templatePath = templatePath;
  }

  public String getTemplateBaseDir() {
    return this.templateBaseDir;
  }

  public void setTemplateBaseDir(final String templateBaseDir) {
    this.templateBaseDir = templateBaseDir;
  }

  public String getTemplateSuffix() {
    return this.templateSuffix;
  }

  public void setTemplateSuffix(final String templateSuffix) {
    this.templateSuffix = templateSuffix;
  }

  public String getReadableTagName() {
    return ofNullable(this.readableTagName).orElse(DEFAULT_READABLE_TAG_NAME);
  }

  public String getDateFormat() {
    return ofNullable(this.dateFormat).orElse(DEFAULT_DATEFORMAT);
  }

  public void setDateFormat(final String dateFormat) {
    this.dateFormat = dateFormat;
  }

  public void setNoIssueName(final String noIssueName) {
    this.noIssueName = noIssueName;
  }

  public void setReadableTagName(final String readableTagName) {
    this.readableTagName = readableTagName;
  }

  public String getNoIssueName() {
    return ofNullable(this.noIssueName).orElse(DEFAULT_NO_ISSUE_NAME);