<commit_before>package filter.expression;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import backend.resource.*;
import filter.ParseException;
import util.Utility;
import backend.interfaces.IModel;
import filter.MetaQualifierInfo;
import filter.QualifierApplicationException;

public class Qualifier implements FilterExpression {

    public static final Qualifier EMPTY = new Qualifier(QualifierType.EMPTY, "");
    public static final Qualifier FALSE = new Qualifier(QualifierType.FALSE, "");

    private final QualifierType type;

    // Only one of these will be present at a time
    private Optional<DateRange> dateRange = Optional.empty();
    private Optional<String> content = Optional.empty();
    private Optional<LocalDate> date = Optional.empty();
    private Optional<NumberRange> numberRange = Optional.empty();
    private Optional<Integer> number = Optional.empty();
    private List<SortKey> sortKeys = new ArrayList<>();

    // Copy constructor
    public Qualifier(Qualifier other) {
        this.type = other.getType();
        if (other.getDateRange().isPresent()) {
            this.dateRange = other.getDateRange();
        } else if (other.getDate().isPresent()) {
            this.date = other.getDate();
        } else if (other.getContent().isPresent()) {
            this.content = other.getContent();
        } else if (other.getNumberRange().isPresent()) {
            this.numberRange = other.getNumberRange();
        } else if (other.getNumber().isPresent()) {
            this.number = other.getNumber();
        } else if (!other.sortKeys.isEmpty()) {
            this.sortKeys = new ArrayList<>(other.sortKeys);
        } else {
            assert false : "Unrecognised content type! You may have forgotten to add it above";
        }
    }

    public Qualifier(QualifierType type, String content) {
        this.type = type;
        this.content = Optional.of(content);
    }

    public Qualifier(QualifierType type, NumberRange numberRange) {
        this.type = type;
        this.numberRange = Optional.of(numberRange);
    }

    public Qualifier(QualifierType type, DateRange dateRange) {
        this.type = type;
        this.dateRange = Optional.of(dateRange);
    }

    public Qualifier(QualifierType type, LocalDate date) {
        this.type = type;
        this.date = Optional.of(date);
    }

    public Qualifier(QualifierType type, int number) {
        this.type = type;
        this.number = Optional.of(number);
    }

    public Qualifier(QualifierType type, List<SortKey> keys) {
        this.type = type;
        this.sortKeys = new ArrayList<>(keys);
    }

    public static FilterExpression replaceMilestoneAliases(IModel model, FilterExpression expr) {
        List<String> repoIds = getMetaQualifierContent(expr, QualifierType.REPO).stream()
                .map(String::toLowerCase)
                .collect(Collectors.toList());

        if (repoIds.isEmpty()) {
            repoIds.add(model.getDefaultRepo().toLowerCase());
        }

        List<TurboMilestone> allMilestones = getMilestonesWithAlias(model, repoIds);
        Optional<Integer> currentMilestoneIndex = getCurrentMilestoneIndex(allMilestones);

        if (!currentMilestoneIndex.isPresent()) {
            return expr;
        }

        return expr.map(q -> {
            if (Qualifier.isMilestoneQualifier(q)) {
                return q.convertMilestoneAliasQualifier(allMilestones, currentMilestoneIndex.get());
            } else {
                return q;
            }
        });
    }

    /**
     * Helper function for testing a filter expression against an issue.
     * Ensures that meta-qualifiers are taken care of.
     * Should always be used over isSatisfiedBy.
     */
    public static boolean process(IModel model, FilterExpression expr, TurboIssue issue) {
        FilterExpression exprWithNormalQualifiers = expr.filter(Qualifier::shouldNotBeStripped);
        List<Qualifier> metaQualifiers = expr.find(Qualifier::isMetaQualifier);

        // Preprocessing for repo qualifier
        boolean containsRepoQualifier = metaQualifiers.stream()
                .map(Qualifier::getType)
                .collect(Collectors.toList())
                .contains(QualifierType.REPO);

        if (!containsRepoQualifier) {
            exprWithNormalQualifiers = new Conjunction(
                new Qualifier(QualifierType.REPO, model.getDefaultRepo()),
                exprWithNormalQualifiers);
        }

        return exprWithNormalQualifiers.isSatisfiedBy(model, issue, new MetaQualifierInfo(metaQualifiers));
    }

    // Get all milestones that can be aliased with the keyword "current".
    private static List<TurboMilestone> getMilestonesWithAlias(IModel model, List<String> repoIds) {
        List<TurboMilestone> milestones = new ArrayList<>();
        // add milestones with due date first
        milestones.addAll(model.getMilestones().stream()
                .filter(ms -> ms.getDueDate().isPresent())
                .filter(ms -> repoIds.contains(ms.getRepoId().toLowerCase()))
                .sorted((a, b) -> getMilestoneDueDateComparator().compare(a, b))
                .collect(Collectors.toList()));

        // Special case: if there is only one open milestone, it should be included regardless of whether
        // it has due date or not.
        // In this case, if it is not included already (the open milestone does not
        // have due date), append it to the milestone list.
        List<TurboMilestone> openMilestones = getOpenMilestones(model, repoIds);
        if (openMilestones.size() == 1 && !openMilestones.get(0).getDueDate().isPresent()) {
            milestones.add(openMilestones.get(0));
        }

        return milestones;
    }

    private static List<TurboMilestone> getOpenMilestones(IModel model, List<String> repoIds) {
        return model.getMilestones().stream()
                .filter(ms -> ms.isOpen())
                .filter(ms -> repoIds.contains(ms.getRepoId().toLowerCase()))
                .collect(Collectors.toList());
    }

    private static Optional<Integer> getCurrentMilestoneIndex(List<TurboMilestone> allMilestones) {
        if (allMilestones.isEmpty()) {
            return Optional.empty();
        }

        Optional<TurboMilestone> openAndRelevantMilestone = allMilestones.stream()
                .filter(ms -> ms.isOpen() && isRelevantMilestone(ms))
                .findFirst();

        if (openAndRelevantMilestone.isPresent()) {
            return Optional.of(allMilestones.indexOf(openAndRelevantMilestone.get()));
        } else {
            //look for open milestone
            int currentIndex = 0;
            for (TurboMilestone checker : allMilestones) {
                if (checker.isOpen()) {
                    return Optional.of(currentIndex);
                }
                currentIndex++;
            }
        }

        // if no open milestone, set current as one after last milestone
        // - this means that no such milestone, which will return no issue
        return Optional.of(allMilestones.size());
    }

    private static boolean isRelevantMilestone(TurboMilestone milestone) {
        // a milestone is relevant if it still has open issues or it is not due yet.
        boolean overdue = milestone.getDueDate().isPresent() &&
                milestone.getDueDate().get().isBefore(LocalDate.now());
        return !(overdue && milestone.getOpenIssues() == 0);
    }

    public static HashSet<String> getMetaQualifierContent(FilterExpression expr, QualifierType qualifierType) {
        HashSet<String> contents = new HashSet<>();
        List<Qualifier> panelMetaQualifiers = expr.find(Qualifier::isMetaQualifier);

        panelMetaQualifiers.forEach(metaQualifier -> {
            if (metaQualifier.getType().equals(qualifierType) && metaQualifier.getContent().isPresent()) {
                contents.add(metaQualifier.getContent().get());
            }
        });

        return contents;
    }

    private static LocalDateTime currentTime = null;

    private static LocalDateTime getCurrentTime() {
        if (currentTime == null) {
            return LocalDateTime.now();
        } else {
            return currentTime;
        }<commit_msg>Fix comments not properly formatted<commit_after>package filter.expression;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import backend.resource.*;
import filter.ParseException;
import util.Utility;
import backend.interfaces.IModel;
import filter.MetaQualifierInfo;
import filter.QualifierApplicationException;

public class Qualifier implements FilterExpression {

    public static final Qualifier EMPTY = new Qualifier(QualifierType.EMPTY, "");
    public static final Qualifier FALSE = new Qualifier(QualifierType.FALSE, "");

    private final QualifierType type;

    // Only one of these will be present at a time
    private Optional<DateRange> dateRange = Optional.empty();
    private Optional<String> content = Optional.empty();
    private Optional<LocalDate> date = Optional.empty();
    private Optional<NumberRange> numberRange = Optional.empty();
    private Optional<Integer> number = Optional.empty();
    private List<SortKey> sortKeys = new ArrayList<>();

    // Copy constructor
    public Qualifier(Qualifier other) {
        this.type = other.getType();
        if (other.getDateRange().isPresent()) {
            this.dateRange = other.getDateRange();
        } else if (other.getDate().isPresent()) {
            this.date = other.getDate();
        } else if (other.getContent().isPresent()) {
            this.content = other.getContent();
        } else if (other.getNumberRange().isPresent()) {
            this.numberRange = other.getNumberRange();
        } else if (other.getNumber().isPresent()) {
            this.number = other.getNumber();
        } else if (!other.sortKeys.isEmpty()) {
            this.sortKeys = new ArrayList<>(other.sortKeys);
        } else {
            assert false : "Unrecognised content type! You may have forgotten to add it above";
        }
    }

    public Qualifier(QualifierType type, String content) {
        this.type = type;
        this.content = Optional.of(content);
    }

    public Qualifier(QualifierType type, NumberRange numberRange) {
        this.type = type;
        this.numberRange = Optional.of(numberRange);
    }

    public Qualifier(QualifierType type, DateRange dateRange) {
        this.type = type;
        this.dateRange = Optional.of(dateRange);
    }

    public Qualifier(QualifierType type, LocalDate date) {
        this.type = type;
        this.date = Optional.of(date);
    }

    public Qualifier(QualifierType type, int number) {
        this.type = type;
        this.number = Optional.of(number);
    }

    public Qualifier(QualifierType type, List<SortKey> keys) {
        this.type = type;
        this.sortKeys = new ArrayList<>(keys);
    }

    public static FilterExpression replaceMilestoneAliases(IModel model, FilterExpression expr) {
        List<String> repoIds = getMetaQualifierContent(expr, QualifierType.REPO).stream()
                .map(String::toLowerCase)
                .collect(Collectors.toList());

        if (repoIds.isEmpty()) {
            repoIds.add(model.getDefaultRepo().toLowerCase());
        }

        List<TurboMilestone> allMilestones = getMilestonesWithAlias(model, repoIds);
        Optional<Integer> currentMilestoneIndex = getCurrentMilestoneIndex(allMilestones);

        if (!currentMilestoneIndex.isPresent()) {
            return expr;
        }

        return expr.map(q -> {
            if (Qualifier.isMilestoneQualifier(q)) {
                return q.convertMilestoneAliasQualifier(allMilestones, currentMilestoneIndex.get());
            } else {
                return q;
            }
        });
    }

    /**
     * Helper function for testing a filter expression against an issue.
     * Ensures that meta-qualifiers are taken care of.
     * Should always be used over isSatisfiedBy.
     */
    public static boolean process(IModel model, FilterExpression expr, TurboIssue issue) {
        FilterExpression exprWithNormalQualifiers = expr.filter(Qualifier::shouldNotBeStripped);
        List<Qualifier> metaQualifiers = expr.find(Qualifier::isMetaQualifier);

        // Preprocessing for repo qualifier
        boolean containsRepoQualifier = metaQualifiers.stream()
                .map(Qualifier::getType)
                .collect(Collectors.toList())
                .contains(QualifierType.REPO);

        if (!containsRepoQualifier) {
            exprWithNormalQualifiers = new Conjunction(
                new Qualifier(QualifierType.REPO, model.getDefaultRepo()),
                exprWithNormalQualifiers);
        }

        return exprWithNormalQualifiers.isSatisfiedBy(model, issue, new MetaQualifierInfo(metaQualifiers));
    }

    /**
     * Get all milestones that can be aliased with the keyword "current".
     */
    private static List<TurboMilestone> getMilestonesWithAlias(IModel model, List<String> repoIds) {
        List<TurboMilestone> milestones = new ArrayList<>();
        // add milestones with due date first
        milestones.addAll(model.getMilestones().stream()
                .filter(ms -> ms.getDueDate().isPresent())
                .filter(ms -> repoIds.contains(ms.getRepoId().toLowerCase()))
                .sorted((a, b) -> getMilestoneDueDateComparator().compare(a, b))
                .collect(Collectors.toList()));

        // Special case: if there is only one open milestone, it should be included regardless of whether
        // it has due date or not.
        // In this case, if it is not included already (the open milestone does not
        // have due date), append it to the milestone list.
        List<TurboMilestone> openMilestones = getOpenMilestones(model, repoIds);
        if (openMilestones.size() == 1) {
            TurboMilestone openMilestone = openMilestones.get(0);
            boolean hasDueDate = openMilestone.getDueDate().isPresent();
            if (!hasDueDate) {
                milestones.add(openMilestone);
            }
        }

        return milestones;
    }

    private static List<TurboMilestone> getOpenMilestones(IModel model, List<String> repoIds) {
        return model.getMilestones().stream()
                .filter(ms -> ms.isOpen())
                .filter(ms -> repoIds.contains(ms.getRepoId().toLowerCase()))
                .collect(Collectors.toList());
    }

    private static Optional<Integer> getCurrentMilestoneIndex(List<TurboMilestone> allMilestones) {
        if (allMilestones.isEmpty()) {
            return Optional.empty();
        }

        Optional<TurboMilestone> openAndRelevantMilestone = allMilestones.stream()
                .filter(ms -> ms.isOpen() && isRelevantMilestone(ms))
                .findFirst();

        if (openAndRelevantMilestone.isPresent()) {
            return Optional.of(allMilestones.indexOf(openAndRelevantMilestone.get()));
        } else {
            //look for open milestone
            int currentIndex = 0;
            for (TurboMilestone checker : allMilestones) {
                if (checker.isOpen()) {
                    return Optional.of(currentIndex);
                }
                currentIndex++;
            }
        }

        // if no open milestone, set current as one after last milestone
        // - this means that no such milestone, which will return no issue
        return Optional.of(allMilestones.size());
    }

    /**
     * Checks whether a milestone still has open issues or it is not due yet.
     */
    private static boolean isRelevantMilestone(TurboMilestone milestone) {
        boolean overdue = milestone.getDueDate().isPresent() &&
                milestone.getDueDate().get().isBefore(LocalDate.now());
        return !(overdue && milestone.getOpenIssues() == 0);
    }

    public static HashSet<String> getMetaQualifierContent(FilterExpression expr, QualifierType qualifierType) {
        HashSet<String> contents = new HashSet<>();
        List<Qualifier> panelMetaQualifiers = expr.find(Qualifier::isMetaQualifier);

        panelMetaQualifiers.forEach(metaQualifier -> {
            if (metaQualifier.getType().equals(qualifierType) && metaQualifier.getContent().isPresent()) {
                contents.add(metaQualifier.getContent().get());
            }
        });

        return contents;
    }

    private static LocalDateTime currentTime = null;

    private static LocalDateTime getCurrentTime() {
        if (currentTime == null) {
            return LocalDateTime.now();
        } else {
            return currentTime;
        }
{'fraction_non_alphanumeric': 0.10371968283582089, 'fraction_numerical': 0.0005247201492537313, 'mean_word_length': 2.5765221017514595, 'pattern_counts': {'":': 0, '<': 59, '<?xml version=': 0, '>': 75, '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': '4546891', 'n_tokens_mistral': 4623, 'n_tokens_neox': 4446, 'n_words': 1071}