<commit_before>package com.conveyal.datatools.editor.datastore;

import com.conveyal.datatools.editor.models.transit.*;
import com.conveyal.datatools.manager.models.FeedSource;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterators;
import java.time.LocalDate;
import org.mapdb.Atomic;
import org.mapdb.BTreeMap;
import org.mapdb.Bind;
import org.mapdb.DB;
import org.mapdb.Fun;
import org.mapdb.Fun.Function2;
import org.mapdb.Fun.Tuple2;
//import play.i18n.Messages;
import com.conveyal.datatools.editor.utils.BindUtils;

import java.util.Collection;
import java.util.Iterator;
import java.util.NavigableSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;

/** a transaction in an agency database */
public class FeedTx extends DatabaseTx {
    // primary com.conveyal.datatools.editor.datastores
    // if you add another, you MUST update SnapshotTx.java
    // if you don't, not only will your new data not be backed up, IT WILL BE THROWN AWAY WHEN YOU RESTORE!
    // AND ALSO the duplicate() function below
    public BTreeMap<String, TripPattern> tripPatterns;
    public BTreeMap<String, Route> routes;
    public BTreeMap<String, Trip> trips;
    public BTreeMap<String, ServiceCalendar> calendars;
    public BTreeMap<String, ScheduleException> exceptions;
    public BTreeMap<String, Stop> stops;
    public BTreeMap<String, Agency> agencies;
    public BTreeMap<String, Fare> fares;
    // if you add anything here, see warning above!

    // secondary indices

    /** Set containing tuples <Route ID, Trip ID> */
    public NavigableSet<Tuple2<String, String>> tripsByRoute;

    /** <route ID, trip pattern ID> */
    public NavigableSet<Tuple2<String, String>> tripPatternsByRoute;

    /** <trip pattern ID, trip ID> */
    public NavigableSet<Tuple2<String, String>> tripsByTripPattern;

    /** <calendar ID, trip ID> */
    public NavigableSet<Tuple2<String, String>> tripsByCalendar;

    /** <calendar id, schedule exception id> */
    public NavigableSet<Tuple2<String, String>> exceptionsByCalendar;

    /** <<patternId, calendarId>, trip id> */
    public NavigableSet<Tuple2<Tuple2<String, String>, String>> tripsByPatternAndCalendar;

    /** major stops for this agency */
    public NavigableSet<String> majorStops;

    /** <Stop ID, Pattern ID> </Stop>trip patterns using each stop */
    public NavigableSet<Tuple2<String, String>> tripPatternsByStop;

    /** number of schedule exceptions on each date - this will always be null, 0, or 1, as we prevent save of others */
    public ConcurrentMap<LocalDate, Long> scheduleExceptionCountByDate;

    /** number of trips on each tuple2<patternId, calendar id> */
    public ConcurrentMap<Tuple2<String, String>, Long> tripCountByPatternAndCalendar;

    /** number of trips on each calendar */
    public ConcurrentMap<String, Long> tripCountByCalendar;

    /**
     * Spatial index of stops. Set<Tuple2<Tuple2<Lon, Lat>, stop ID>>
     * This is not a true spatial index, but should be sufficiently efficient for our purposes.
     * Jan Kotek describes this approach here, albeit in Czech: https://groups.google.com/forum/#!msg/mapdb/ADgSgnXzkk8/Q8J9rWAWXyMJ
     */
    public NavigableSet<Tuple2<Tuple2<Double, Double>, String>> stopsGix;

    /** snapshot versions. we use an atomic value so that they are (roughly) sequential, instead of using unordered UUIDs */
    private Atomic.Integer snapshotVersion;

    /**
     * Create an agency tx.
     */
    public FeedTx(DB tx) {
        this(tx, true);
    }

    /** Create an agency tx, optionally without secondary indices */
    public FeedTx(DB tx, boolean buildSecondaryIndices) {
        super(tx);

        tripPatterns = getMap("tripPatterns");
        routes = getMap("routes");
        trips = getMap("trips");
        calendars = getMap("calendars");
        exceptions = getMap("exceptions");
        snapshotVersion = tx.getAtomicInteger("snapshotVersion");
        stops = getMap("stops");
        agencies = getMap("agencies");
        fares = getMap("fares");

        if (buildSecondaryIndices)
            buildSecondaryIndices();
    }

    public void buildSecondaryIndices () {
        // build secondary indices
        // we store indices in the mapdb not because we care about persistence, but because then they
        // will be managed within the context of MapDB transactions
        tripsByRoute = getSet("tripsByRoute");

        // bind the trips to the routes
        Bind.secondaryKeys(trips, tripsByRoute, new Fun.Function2<String[], String, Trip>() {

            @Override
            public String[] run(String tripId, Trip trip) {
                return new String[] { trip.routeId };
            }
        });

        tripPatternsByRoute = getSet("tripPatternsByRoute");
        Bind.secondaryKeys(tripPatterns, tripPatternsByRoute, new Fun.Function2<String[], String, TripPattern>() {

            @Override
            public String[] run(String tripId, TripPattern trip) {
                // TODO Auto-generated method stub
                return new String[] { trip.routeId };
            }
        });

        tripsByTripPattern = getSet("tripsByTripPattern");
        Bind.secondaryKeys(trips, tripsByTripPattern, new Fun.Function2<String[], String, Trip> () {

            @Override
            public String[] run(String tripId, Trip trip) {
                // TODO Auto-generated method stub<commit_msg>beginnings of editedSinceSnapshot prop for FeedTx<commit_after>package com.conveyal.datatools.editor.datastore;

import com.conveyal.datatools.editor.models.transit.*;
import com.conveyal.datatools.manager.models.FeedSource;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterators;
import java.time.LocalDate;
import org.mapdb.Atomic;
import org.mapdb.BTreeMap;
import org.mapdb.Bind;
import org.mapdb.DB;
import org.mapdb.Fun;
import org.mapdb.Fun.Function2;
import org.mapdb.Fun.Tuple2;
//import play.i18n.Messages;
import com.conveyal.datatools.editor.utils.BindUtils;

import java.util.Collection;
import java.util.Iterator;
import java.util.NavigableSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;

/** a transaction in an agency database */
public class FeedTx extends DatabaseTx {
    // primary com.conveyal.datatools.editor.datastores
    // if you add another, you MUST update SnapshotTx.java
    // if you don't, not only will your new data not be backed up, IT WILL BE THROWN AWAY WHEN YOU RESTORE!
    // AND ALSO the duplicate() function below
    public BTreeMap<String, TripPattern> tripPatterns;
    public BTreeMap<String, Route> routes;
    public BTreeMap<String, Trip> trips;
    public BTreeMap<String, ServiceCalendar> calendars;
    public BTreeMap<String, ScheduleException> exceptions;
    public BTreeMap<String, Stop> stops;
    public BTreeMap<String, Agency> agencies;
    public BTreeMap<String, Fare> fares;
    // if you add anything here, see warning above!

    // secondary indices

    /** Set containing tuples <Route ID, Trip ID> */
    public NavigableSet<Tuple2<String, String>> tripsByRoute;

    /** <route ID, trip pattern ID> */
    public NavigableSet<Tuple2<String, String>> tripPatternsByRoute;

    /** <trip pattern ID, trip ID> */
    public NavigableSet<Tuple2<String, String>> tripsByTripPattern;

    /** <calendar ID, trip ID> */
    public NavigableSet<Tuple2<String, String>> tripsByCalendar;

    /** <calendar id, schedule exception id> */
    public NavigableSet<Tuple2<String, String>> exceptionsByCalendar;

    /** <<patternId, calendarId>, trip id> */
    public NavigableSet<Tuple2<Tuple2<String, String>, String>> tripsByPatternAndCalendar;

    /** major stops for this agency */
    public NavigableSet<String> majorStops;

    /** <Stop ID, Pattern ID> </Stop>trip patterns using each stop */
    public NavigableSet<Tuple2<String, String>> tripPatternsByStop;

    /** number of schedule exceptions on each date - this will always be null, 0, or 1, as we prevent save of others */
    public ConcurrentMap<LocalDate, Long> scheduleExceptionCountByDate;

    /** number of trips on each tuple2<patternId, calendar id> */
    public ConcurrentMap<Tuple2<String, String>, Long> tripCountByPatternAndCalendar;

    /** number of trips on each calendar */
    public ConcurrentMap<String, Long> tripCountByCalendar;

    /**
     * Spatial index of stops. Set<Tuple2<Tuple2<Lon, Lat>, stop ID>>
     * This is not a true spatial index, but should be sufficiently efficient for our purposes.
     * <NAME> describes this approach here, albeit in Czech: https://groups.google.com/forum/#!msg/mapdb/ADgSgnXzkk8/Q8J9rWAWXyMJ
     */
    public NavigableSet<Tuple2<Tuple2<Double, Double>, String>> stopsGix;

    /** snapshot versions. we use an atomic value so that they are (roughly) sequential, instead of using unordered UUIDs */
    private Atomic.Integer snapshotVersion;

//    public Atomic.Boolean editedSinceSnapshot;
    /**
     * Create an agency tx.
     */
    public FeedTx(DB tx) {
        this(tx, true);
    }

    /** Create an agency tx, optionally without secondary indices */
    public FeedTx(DB tx, boolean buildSecondaryIndices) {
        super(tx);

        tripPatterns = getMap("tripPatterns");
        routes = getMap("routes");
        trips = getMap("trips");
        calendars = getMap("calendars");
        exceptions = getMap("exceptions");
        snapshotVersion = tx.getAtomicInteger("snapshotVersion");
        stops = getMap("stops");
        agencies = getMap("agencies");
        fares = getMap("fares");

        if (buildSecondaryIndices)
            buildSecondaryIndices();

//        editedSinceSnapshot = tx.getAtomicBoolean("editedSinceSnapshot") == null ? tx.createAtomicBoolean("editedSinceSnapshot", false) : tx.;
    }
    public void commit () {
        try {
//            editedSinceSnapshot.set(true);
            tx.commit();
        } catch (UnsupportedOperationException e) {
            // probably read only, but warn
            LOG.warn("Rollback failed; if this is a read-only database this is not unexpected");
        }        closed = true;
    }
    public void buildSecondaryIndices () {
        // build secondary indices
        // we store indices in the mapdb not because we care about persistence, but because then they
        // will be managed within the context of MapDB transactions
        tripsByRoute = getSet("tripsByRoute");

        // bind the trips to the routes
        Bind.secondaryKeys(trips, tripsByRoute, new Fun.Function2<String[], String, Trip>() {

            @Override
            public String[] run(String tripId, Trip trip) {
                return new String[] { trip.routeId };
            }
        });

        tripPatternsByRoute = getSet("tripPatternsByRoute");
        Bind.secondaryKeys(tripPatterns, tripPatternsByRoute, new Fun.Function2<String[], String, TripPattern>() {

            @Override
            public String[] run(String tripId, TripPattern trip) {
                // TODO Auto-generated method stub
                return new String[] { trip.routeId };
            }
        });

        tripsByTripPattern = getSet("tripsByTripPattern");
        Bind.secondaryKeys(trips, tripsByTripPattern, new Fun.Function2<String[], String, Trip> () {

            @Override
            public String[] run(String tripId, Trip trip) {
                // TODO Auto-generated method stub
{'fraction_non_alphanumeric': 0.1034542017528785, 'fraction_numerical': 0.004639972503866644, 'mean_word_length': 3.17467718794835, 'pattern_counts': {'":': 0, '<': 98, '<?xml version=': 0, '>': 98, 'https://': 2, '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': '4230091', 'n_tokens_mistral': 3363, 'n_tokens_neox': 3261, 'n_words': 960}