// // $Id$ // // Vilya library - tools for developing networked games // Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved // http://www.threerings.net/code/vilya/ // // This library is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published // by the Free Software Foundation; either version 2.1 of the License, or // (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package com.threerings.stats.data; import java.util.Iterator; import com.threerings.presents.dobj.DSet; /** * A distributed set containing {@link Stat} objects. */ public final class StatSet extends DSet { /** An interface to be implemented by an entity that wishes to be notified when the contents of * a stat set change. */ public interface Container { public void addToStats (Stat stat); public void updateStats (Stat stat); } /** Creates a stat set with the specified contents. */ public StatSet (Iterator contents) { super(contents); } /** Creates a blank stat set. */ public StatSet () { } /** * Wires this stat set up to a containing user object. All subsequent modifications will be * published to the container. */ public void setContainer (Container container) { _container = container; } /** * Updates a stat in this set, using the supplied StatModifier. This function should * only be called after a successful stat modification has been made to the StatRepository. * It will increment the Stat's modCount, and won't set its dirty bit if it's not already * set, to prevent the Stat from being re-written to the repo unnecessarily. */ public void syncStat (StatModifier modifier) { boolean wasModified = false; @SuppressWarnings("unchecked") T stat = (T)getStat(modifier.getType()); if (stat != null) { wasModified = stat.isModified(); modifier.modify(stat); updateStat(stat); } else { @SuppressWarnings("unchecked") T nstat = (T)modifier.getType().newStat(); stat = nstat; modifier.modify(stat); addStat(stat); } stat.setModified(wasModified); stat.setModCount((byte)((stat.getModCount() + 1) % Byte.MAX_VALUE)); } /** * Sets an integer statistic in this set. * * @exception ClassCastException thrown if the registered type of the specified stat is not an * {@link IntStat}. */ public void setStat (Stat.Type type, int value) { IntStat stat = (IntStat)getStat(type); if (stat == null) { stat = (IntStat)type.newStat(); stat.setValue(value); addStat(stat); } else if (stat.setValue(value)) { updateStat(stat); } } /** * Sets an integer stat to the specified value, if it exceeds our existing recorded value. * * @exception ClassCastException thrown if the registered type of the specified stat is not an * {@link IntStat}. */ public void maxStat (Stat.Type type, int value) { int ovalue = getIntStat(type); if (value > ovalue) { setStat(type, value); } } /** * Increments an integer statistic in this set. * * @exception ClassCastException thrown if the registered type of the specified stat is not an * {@link IntStat}. */ public void incrementStat (Stat.Type type, int delta) { IntStat stat = (IntStat)getStat(type); if (stat == null) { stat = (IntStat)type.newStat(); stat.increment(delta); addStat(stat); } else if (stat.increment(delta)) { updateStat(stat); } } /** * Appends an integer value to an {@link IntArrayStat}. * * @exception ClassCastException thrown if the registered type of the specified stat is not an * {@link IntArrayStat}. */ public void appendStat (Stat.Type type, int value) { IntArrayStat stat = (IntArrayStat)getStat(type); if (stat == null) { stat = (IntArrayStat)type.newStat(); stat.appendValue(value); addStat(stat); } else { stat.appendValue(value); updateStat(stat); } } /** * Adds a value to a {@link SetStat}. * * @exception ClassCastException thrown if the registered type of the specified stat is not * an {@link SetStat} parameterized on the given type. */ public void addToSetStat (Stat.Type type, T value) { @SuppressWarnings("unchecked") SetStat stat = (SetStat)getStat(type); if (stat == null) { stat = (SetStat)type.newStat(); stat.add(value); addStat(stat); } else if (stat.add(value)) { updateStat(stat); } } /** * Increments a string value in a {@link StringMapStat}. * * @exception ClassCastException thrown if the registered type of the specified stat is not an * {@link StringMapStat}. */ public void incrementMapStat (Stat.Type type, String value, int amount) { StringMapStat stat = (StringMapStat)getStat(type); if (stat == null) { stat = (StringMapStat)type.newStat(); stat.increment(value, amount); addStat(stat); } else if (stat.increment(value, amount)) { updateStat(stat); } } /** * Returns the current value of the specified integer statistic. * * @exception ClassCastException thrown if the registered type of the specified stat is not an * {@link IntStat}. */ public int getIntStat (Stat.Type type) { IntStat stat = (IntStat)getStat(type); return (stat == null) ? 0 : stat.getValue(); } /** * Returns the maximum value by which the specified integer statistic has ever been * incremented. * * @exception ClassCastException thrown if the registered type of the specified stat is not an * {@link MaxIntStat}. */ public int getMaxIntStat (Stat.Type type) { MaxIntStat stat = (MaxIntStat)getStat(type); return (stat == null) ? 0 : stat.getMaxValue(); } /** * Returns the current value of the specified integer array statistic. * * @exception ClassCastException thrown if the registered type of the specified stat is not an * {@link IntArrayStat}. */ public int[] getIntArrayStat (Stat.Type type) { IntArrayStat stat = (IntArrayStat)getStat(type); return (stat == null) ? new int[0] : stat.getValue(); } /** * Returns the current size of the specified SetStat statistic. * * @exception ClassCastException thrown if the registered type of the specified stat is not an * {@link SetStat}. */ public int getSetStatSize (Stat.Type type) { @SuppressWarnings("unchecked") SetStat stat = (SetStat)getStat(type); return (stat == null ? 0 : stat.size()); } /** * Returns true if the specified {@link SetStat} contains the specified value, false * otherwise. */ public boolean containsValue (Stat.Type type, T value) { @SuppressWarnings("unchecked") SetStat stat = (SetStat)getStat(type); return (stat == null) ? false : stat.contains(value); } /** * Returns the value to which the specified string is mapped in a {@link StringMapStat} or zero * if the value has not been mapped. */ public int getMapValue (Stat.Type type, String value) { StringMapStat stat = (StringMapStat)getStat(type); return (stat == null) ? 0 : stat.get(value); } /** * Don't call this method, it's only needed by some tricky business we do when preventing * distributed object event generation for all but the bounty criteria related stats. */ public void addQuietly (Stat stat) { add(stat); } protected void addStat (Stat stat) { if (_container != null) { _container.addToStats(stat); } else { add(stat); } } protected void updateStat (Stat stat) { if (_container != null) { _container.updateStats(stat); } } /** * Returns the Stat of the given type, if it exists in the set, and null otherwise. */ protected Stat getStat (Stat.Type type) { return get(type.name()); } protected transient Container _container; }