<commit_before>//
// Triple Play - utilities for use in PlayN-based games
// Copyright (c) 2011-2012, Three Rings Design, Inc. - All rights reserved.
// http://github.com/threerings/tripleplay/blob/master/LICENSE

package tripleplay.syncdb;

import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import react.AbstractValue;
import react.RMap;
import react.RSet;
import react.Slot;
import react.Value;

import playn.core.Asserts;
import playn.core.Platform;
import playn.core.Storage;

import static tripleplay.syncdb.Log.log;

/**
 * A database of key/value pairs that is synced (via a server) across multiple devices. The
 * suggested usage pattern is to subclass {@code SyncDB} and define your properties like so:
 * <pre>{@code
 * public class FooDB extends SyncDB {
 *   public final Value<String> name = value("name", (String)null, Codec.STRING, Resolver.SERVER);
 *   public final Value<Integer> xp = value("xp", 0, Codec.INT, Resolver.INTMAX);
 *   public final Value<Integer> difficultyLevel = value("diff", 0, Codec.INT, Resolver.SERVER);
 *   public final RSet<String> badges = set("badges", Codec.STRING, SetResolver.UNION);
 *   public final RMap<String,Integer> items = map("it", Codec.STRING, Codec.INT, Resolver.INTMAX);
 *   // etc.
 * }
 * }</pre>
 */
public abstract class SyncDB
{
    /**
     * Returns the version at which this database was last synced.
     */
    public int version () {
        return _version;
    }

    /**
     * Returns true if this database contains changes that have not been synced.
     */
    public boolean hasUnsyncedChanges () {
        return !_mods.isEmpty();
    }

    /**
     * Returns a map of properties that have changed since our last sync. These can be sent to the
     * server (along with our current version) to sync our state with the server.
     */
    public Map<String,String> getDelta () {
        Map<String,String> delta = new HashMap<String,String>();
        for (String name : _mods) delta.put(name, _storage.getItem(name));
        return delta;
    }

    /**
     * Notes that we synced cleanly with the server. Updates our local version to the latest sync
     * version and notes that we no longer have unsynced modifications.
     */
    public void noteSync (int version) {
        _mods.clear();
        flushMods();
        updateVersion(version);
    }

    /**
     * Returns whether the supplied delta contains changes to any properties for which unsynced
     * changes also exist.
     */
    public boolean containsMerges (Map<String,String> delta) {
        for (String key : delta.keySet()) if (_mods.contains(key)) return true;
        return false;
    }

    /**
     * Applies the supplied changes to this database. Any conflicts will be resolved according to
     * the configured policies. If the resolved value differs from the supplied value, the property
     * will remain marked as locally modified. Thus changes may need to be flushed again after
     * calling this method. After applying the delta and resolving conflicts, the local version
     * will be updated to the supplied version.
     *
     * @param version the latest version.
     * @param delta the modifications from our local version to the latest version.
     */
    public void applyDelta (int version, Map<String,String> delta) {
        // resolve all of the subdbs that are needed to apply these properties
        Set<String> subDBs = new HashSet<String>();
        for (String key : delta.keySet()) {
            String sdb = DBUtil.subDB(key);
            if (sdb != null) subDBs.add(sdb);
        }
        if (!subDBs.isEmpty()) for (String subdb : subDBs) getSubDB(subdb);

        // now apply the delta to the appropriate properties
        for (Map.Entry<String,String> entry : delta.entrySet()) {
            String name = entry.getKey();
            Property prop;
            int pidx = name.indexOf(DBUtil.MAP_KEY_SEP);
            if (pidx == -1) prop = _props.get(name);
            else prop = _props.get(name.substring(0, pidx));
            if (prop == null) {
                log.warning("No local property defined", "name", name);
            } else if (_mods.contains(name)) {
                if (prop.merge(name, entry.getValue())) _mods.remove(name);
            } else {
                prop.update(name, entry.getValue());
                _mods.remove(name); // updating will cause the property to be marked as locally
                                    // changed, but it's not really locally changed, it's been set
                                    // to the latest synced value, so clear the mod flag
            }
        }

        flushMods();
        updateVersion(version);
    }

    /**
     * Prepares this database to be melded into a pre-existing database. Resets this database's
     * version to zero and marks all properties as modified. The database can then be synced with
     * another database which will merge the other database into this one and then push remaining
     * changes back up to the server. This is used to merge the database between two clients.
     */
    public void prepareToMeld () {
        updateVersion(0);<commit_msg>Don't let a choking client break the sync process.<commit_after>//
// Triple Play - utilities for use in PlayN-based games
// Copyright (c) 2011-2012, Three Rings Design, Inc. - All rights reserved.
// http://github.com/threerings/tripleplay/blob/master/LICENSE

package tripleplay.syncdb;

import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import react.AbstractValue;
import react.RMap;
import react.RSet;
import react.Slot;
import react.Value;

import playn.core.Asserts;
import playn.core.Platform;
import playn.core.Storage;

import static tripleplay.syncdb.Log.log;

/**
 * A database of key/value pairs that is synced (via a server) across multiple devices. The
 * suggested usage pattern is to subclass {@code SyncDB} and define your properties like so:
 * <pre>{@code
 * public class FooDB extends SyncDB {
 *   public final Value<String> name = value("name", (String)null, Codec.STRING, Resolver.SERVER);
 *   public final Value<Integer> xp = value("xp", 0, Codec.INT, Resolver.INTMAX);
 *   public final Value<Integer> difficultyLevel = value("diff", 0, Codec.INT, Resolver.SERVER);
 *   public final RSet<String> badges = set("badges", Codec.STRING, SetResolver.UNION);
 *   public final RMap<String,Integer> items = map("it", Codec.STRING, Codec.INT, Resolver.INTMAX);
 *   // etc.
 * }
 * }</pre>
 */
public abstract class SyncDB
{
    /**
     * Returns the version at which this database was last synced.
     */
    public int version () {
        return _version;
    }

    /**
     * Returns true if this database contains changes that have not been synced.
     */
    public boolean hasUnsyncedChanges () {
        return !_mods.isEmpty();
    }

    /**
     * Returns a map of properties that have changed since our last sync. These can be sent to the
     * server (along with our current version) to sync our state with the server.
     */
    public Map<String,String> getDelta () {
        Map<String,String> delta = new HashMap<String,String>();
        for (String name : _mods) delta.put(name, _storage.getItem(name));
        return delta;
    }

    /**
     * Notes that we synced cleanly with the server. Updates our local version to the latest sync
     * version and notes that we no longer have unsynced modifications.
     */
    public void noteSync (int version) {
        _mods.clear();
        flushMods();
        updateVersion(version);
    }

    /**
     * Returns whether the supplied delta contains changes to any properties for which unsynced
     * changes also exist.
     */
    public boolean containsMerges (Map<String,String> delta) {
        for (String key : delta.keySet()) if (_mods.contains(key)) return true;
        return false;
    }

    /**
     * Applies the supplied changes to this database. Any conflicts will be resolved according to
     * the configured policies. If the resolved value differs from the supplied value, the property
     * will remain marked as locally modified. Thus changes may need to be flushed again after
     * calling this method. After applying the delta and resolving conflicts, the local version
     * will be updated to the supplied version.
     *
     * @param version the latest version.
     * @param delta the modifications from our local version to the latest version.
     */
    public void applyDelta (int version, Map<String,String> delta) {
        // resolve all of the subdbs that are needed to apply these properties
        Set<String> subDBs = new HashSet<String>();
        for (String key : delta.keySet()) {
            String sdb = DBUtil.subDB(key);
            if (sdb != null) subDBs.add(sdb);
        }
        if (!subDBs.isEmpty()) for (String subdb : subDBs) getSubDB(subdb);

        // now apply the delta to the appropriate properties
        for (Map.Entry<String,String> entry : delta.entrySet()) {
            String name = entry.getKey(), value = entry.getValue();
            Property prop;
            int pidx = name.indexOf(DBUtil.MAP_KEY_SEP);
            if (pidx == -1) prop = _props.get(name);
            else prop = _props.get(name.substring(0, pidx));
            if (prop == null) {
                log.warning("No local property defined", "name", name);
            } else if (_mods.contains(name)) {
                try {
                    if (prop.merge(name, value)) _mods.remove(name);
                } catch (Exception e) {
                    log.warning("Property merge fail", "name", name, "value", value, e);
                }
            } else {
                try {
                    prop.update(name, value);
                } catch (Exception e) {
                    log.warning("Property update fail", "name", name, "value", value, e);
                }
                _mods.remove(name); // updating will cause the property to be marked as locally
                                    // changed, but it's not really locally changed, it's been set
                                    // to the latest synced value, so clear the mod flag
            }
        }

        flushMods();
        updateVersion(version);
    }

    /**
     * Prepares this database to be melded into a pre-existing database. Resets this database's
     * version to zero and marks all properties as modified. The database can then be synced with
     * another database which will merge the other database into this one and then push remaining
     * changes back up to the server. This is used to merge the database between two clients.
     */
    public void prepareToMeld () {
        updateVersion(0);
{'fraction_non_alphanumeric': 0.09694969675347842, 'fraction_numerical': 0.00231894398858366, 'mean_word_length': 2.519460138104206, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '407604', 'n_tokens_mistral': 3074, 'n_tokens_neox': 3004, 'n_words': 1137}