<commit_before>package com.holmsted.gerrit;

import org.joda.time.DateTime;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import javax.annotation.Nonnull;

import static com.google.common.base.Preconditions.checkNotNull;

public class DatedList<T> extends ArrayList<T> {
    private static final int[] MONTHS_IN_YEAR = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };

    private long minDate = Long.MAX_VALUE;
    private long maxDate = Long.MIN_VALUE;

    @Nonnull
    private final DateTimeProvider<T> dateTimeProvider;

    private final Hashtable<Integer, YearlyItemList<T>> itemsPerYear = new Hashtable<>();

    public DatedList(@Nonnull DateTimeProvider<T> dateTimeProvider) {
        this.dateTimeProvider = dateTimeProvider;
    }

    @Override
    public boolean add(T item) {
        long date = dateTimeProvider.getDate(checkNotNull(item));
        DateTime itemDateTime = new DateTime(date);
        int year = itemDateTime.getYear();

        minDate = Math.min(minDate, date);
        maxDate = Math.max(maxDate, date);

        YearlyItemList<T> yearlyItems = itemsPerYear.get(year);
        if (yearlyItems == null) {
            yearlyItems = new YearlyItemList<>(dateTimeProvider);
        }
        yearlyItems.add(item);
        itemsPerYear.put(year, yearlyItems);
        return super.add(item);
    }

    public List<Integer> getYearRange() {
        List<Integer> years = new ArrayList<>();
        if (minDate == Long.MAX_VALUE || maxDate == Long.MIN_VALUE) {
            return years;
        }
        int minYear = new DateTime(minDate).getYear();
        int maxYear = new DateTime(maxDate).getYear();
        for (int year = minYear; year <= maxYear; ++year) {
            years.add(year);
        }
        return years;
    }

    public YearlyItemList<T> getItemsForYear(int year) {
        return itemsPerYear.get(year);
    }

    public String getDisplayableMonthOnMonthChange(int year, int month) {
        if (!isDateWithinRange(year, month)) {
            return "";
        }

        if (month > 1) {
            return itemsPerYear.get(year).getDisplayableMonthOnMonthChange(month);
        }

        YearlyItemList items = itemsPerYear.get(year);
        YearlyItemList prevYearItems = itemsPerYear.get(year - 1);
        if (prevYearItems == null || items == null) {
            return MonthlyTimeFormat.formatFloat(Float.NaN);
        } else {
            int itemsForLastMonthOfPrevYear = prevYearItems.getMonthlyItemCount(12);
            int itemsForFirstMonth = items.getMonthlyItemCount(1);
            return MonthlyTimeFormat.formatRateOfChange(itemsForLastMonthOfPrevYear, itemsForFirstMonth);
        }
    }

    public String getDisplayableQuarterOnQuarterChange(int year, int month) {
        if (!isDateWithinRange(year, month)) {
            return "";
        }

        int quarter = MonthlyTimeFormat.monthToQuarter(month);
        if (quarter > 0) {
            return itemsPerYear.get(year).getDisplayableQuarterOnQuarterChange(month);
        }

        YearlyItemList items = itemsPerYear.get(year);
        YearlyItemList prevYearItems = itemsPerYear.get(year - 1);
        if (prevYearItems == null || items == null) {
            return MonthlyTimeFormat.formatFloat(Float.NaN);
        } else {
            int itemsForLastQuarterOfPrevYear = prevYearItems.getQuarterlyItemCount(3);
            int itemsForFirstQuarter = items.getQuarterlyItemCount(0);
            return MonthlyTimeFormat.formatRateOfChange(itemsForLastQuarterOfPrevYear, itemsForFirstQuarter);
        }
    }

    public String getPrintableMonthlyItemCount(int year, int month) {
        if (!isDateWithinRange(year, month)) {
            return "";
        }
        YearlyItemList<T> ts = itemsPerYear.get(year);
        return itemsPerYear.get(year).getPrintableMonthlyItemCount(month);
    }

    public static int[] getMonthsInYear() {
        return MONTHS_IN_YEAR;
    }

    private boolean isDateWithinRange(int year, int month) {
        if (minDate == Long.MAX_VALUE || maxDate == Long.MIN_VALUE) {
            return false;
        }
<commit_msg>DatedList: don't crash in cases there's gaps in commit years<commit_after>package com.holmsted.gerrit;

import org.joda.time.DateTime;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import javax.annotation.Nonnull;

import static com.google.common.base.Preconditions.checkNotNull;

public class DatedList<T> extends ArrayList<T> {
    private static final int[] MONTHS_IN_YEAR = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };

    private long minDate = Long.MAX_VALUE;
    private long maxDate = Long.MIN_VALUE;

    @Nonnull
    private final DateTimeProvider<T> dateTimeProvider;

    private final Hashtable<Integer, YearlyItemList<T>> itemsPerYear = new Hashtable<>();

    public DatedList(@Nonnull DateTimeProvider<T> dateTimeProvider) {
        this.dateTimeProvider = dateTimeProvider;
    }

    @Override
    public boolean add(T item) {
        long date = dateTimeProvider.getDate(checkNotNull(item));
        DateTime itemDateTime = new DateTime(date);
        int year = itemDateTime.getYear();

        minDate = Math.min(minDate, date);
        maxDate = Math.max(maxDate, date);

        YearlyItemList<T> yearlyItems = itemsPerYear.get(year);
        if (yearlyItems == null) {
            yearlyItems = new YearlyItemList<>(dateTimeProvider);
        }
        yearlyItems.add(item);
        itemsPerYear.put(year, yearlyItems);
        return super.add(item);
    }

    public List<Integer> getYearRange() {
        List<Integer> years = new ArrayList<>();
        if (minDate == Long.MAX_VALUE || maxDate == Long.MIN_VALUE) {
            return years;
        }
        int minYear = new DateTime(minDate).getYear();
        int maxYear = new DateTime(maxDate).getYear();
        for (int year = minYear; year <= maxYear; ++year) {
            years.add(year);
        }
        return years;
    }

    public YearlyItemList<T> getItemsForYear(int year) {
        return itemsPerYear.get(year);
    }

    public String getDisplayableMonthOnMonthChange(int year, int month) {
        if (!isDateWithinRange(year, month)) {
            return "";
        }
        YearlyItemList<T> items = itemsPerYear.get(year);
        if (items == null) {
            return MonthlyTimeFormat.formatFloat(Float.NaN);
        }

        if (month > 1) {
            return items.getDisplayableMonthOnMonthChange(month);
        }

        YearlyItemList prevYearItems = itemsPerYear.get(year - 1);
        if (prevYearItems == null) {
            return MonthlyTimeFormat.formatFloat(Float.NaN);
        } else {
            int itemsForLastMonthOfPrevYear = prevYearItems.getMonthlyItemCount(12);
            int itemsForFirstMonth = items.getMonthlyItemCount(1);
            return MonthlyTimeFormat.formatRateOfChange(itemsForLastMonthOfPrevYear, itemsForFirstMonth);
        }
    }

    public String getDisplayableQuarterOnQuarterChange(int year, int month) {
        if (!isDateWithinRange(year, month)) {
            return "";
        }
        YearlyItemList<T> items = itemsPerYear.get(year);
        if (items == null) {
            return MonthlyTimeFormat.formatFloat(Float.NaN);
        }

        int quarter = MonthlyTimeFormat.monthToQuarter(month);
        if (quarter > 0) {
            return items.getDisplayableQuarterOnQuarterChange(month);
        }

        YearlyItemList prevYearItems = itemsPerYear.get(year - 1);
        if (prevYearItems == null) {
            return MonthlyTimeFormat.formatFloat(Float.NaN);
        } else {
            int itemsForLastQuarterOfPrevYear = prevYearItems.getQuarterlyItemCount(3);
            int itemsForFirstQuarter = items.getQuarterlyItemCount(0);
            return MonthlyTimeFormat.formatRateOfChange(itemsForLastQuarterOfPrevYear, itemsForFirstQuarter);
        }
    }

    public String getPrintableMonthlyItemCount(int year, int month) {
        if (!isDateWithinRange(year, month)) {
            return "";
        }
        YearlyItemList<T> itemsPerYear = this.itemsPerYear.get(year);
        if (itemsPerYear == null) {
            // When used with templates, this is only possible
            // if someone committed e.g. in 2013, and in 2015, but not in 2014
            return "0";
        } else {
            return itemsPerYear.getPrintableMonthlyItemCount(month);
        }
    }

    public static int[] getMonthsInYear() {
        return MONTHS_IN_YEAR;
    }

    private boolean isDateWithinRange(int year, int month) {
        if (minDate == Long.MAX_VALUE || maxDate == Long.MIN_VALUE) {
            return false;
        }

{'fraction_non_alphanumeric': 0.09927694249971307, 'fraction_numerical': 0.0070010329392861245, 'mean_word_length': 2.6429765886287626, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3742060', 'n_tokens_mistral': 2597, 'n_tokens_neox': 2480, 'n_words': 548}