Second iteration on the workaround infrastructure (not in use).

* Rename interfaces to I...
* Split off the statistics counting to accept/deny counters.
* Remove support for parent workarounds.
* Do use a testUse method for overriding in AbstractWorkaround.
* Add a stage counter to WorkaroundCountDown, for meta checks per stage.
* Extend/alter/implement default method signatures and interfaces.

Missing:
* Tests for the workaround package.
* Add a primary thread / moving checks registry instance.
* Add default sets and use in moving checks (primary thread only).
This commit is contained in:
asofold 2016-01-17 16:20:18 +01:00
parent f337538ce3
commit d2b151af99
16 changed files with 552 additions and 182 deletions

View File

@ -0,0 +1,58 @@
package fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny;
/**
* Default implementation for accept-deny counters,
*
* @author asofold
*
*/
public class AcceptDenyCounter implements IResettableAcceptDenyCounter, ICounterWithParent {
private int acceptCount = 0;
private int denyCount = 0;
private IAcceptDenyCounter parent = null;
@Override
public AcceptDenyCounter setParentCounter(IAcceptDenyCounter parent) {
this.parent = parent;
return this;
}
@Override
public IAcceptDenyCounter getParentCounter() {
return parent;
}
@Override
public void accept() {
acceptCount ++;
if (parent != null) {
parent.accept();
}
}
@Override
public int getAcceptCount() {
return acceptCount;
}
@Override
public void deny() {
denyCount ++;
if (parent != null) {
parent.deny();
}
}
@Override
public int getDenyCount() {
return denyCount;
}
@Override
public void resetCounter() {
acceptCount = denyCount = 0;
}
}

View File

@ -0,0 +1,24 @@
package fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny;
/**
* Count the number of times of accepting something.
*
* @author asofold
*
*/
public interface IAcceptCounter {
/**
* Increase the accept count. Propagate to parent (if any).
*/
public void accept();
/**
* Get the number of times, that accept() has been called (since last reset,
* if resettable).
*
* @return
*/
public int getAcceptCount();
}

View File

@ -0,0 +1,11 @@
package fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny;
/**
* Just combining accept and deny counts.
*
* @author asofold
*
*/
public interface IAcceptDenyCounter extends IAcceptCounter, IDenyCounter {
}

View File

@ -0,0 +1,20 @@
package fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny;
public interface ICounterWithParent {
/**
* Set a parent counter.
*
* @param parent
* @return This (counter) instance for chaining (not the previous parent).
*/
public IAcceptDenyCounter setParentCounter(IAcceptDenyCounter parent);
/**
* Retrieve the parent counter.
*
* @return
*/
public IAcceptDenyCounter getParentCounter();
}

View File

@ -0,0 +1,24 @@
package fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny;
/**
* Count the number of times of denying something.
*
* @author asofold
*
*/
public interface IDenyCounter {
/**
* Increase the deny count. Propagate to parent, if any.
*/
public void deny();
/**
* Get the number of times, that deny() has been called (since last reset,
* if resettable).
*
* @return
*/
public int getDenyCount();
}

View File

@ -0,0 +1,10 @@
package fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny;
/**
* Juts combine all three.
* @author asofold
*
*/
public interface IResettableAcceptDenyCounter extends IAcceptDenyCounter, IResettableCounter {
}

View File

@ -0,0 +1,17 @@
package fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny;
/**
* Allow resetting counters.
*
* @author asofold
*
*/
public interface IResettableCounter {
/**
* Reset all contained counters. Not propagated to parent.
*
*/
public void resetCounter();
}

View File

@ -1,36 +1,27 @@
package fr.neatmonster.nocheatplus.workaround;
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.AcceptDenyCounter;
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.IAcceptDenyCounter;
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.ICounterWithParent;
/**
* Implementing the minimum features for counting use, plus the ability to trigger a parent
* count.
* Implementing the minimum features for counting use.
*
* @author asofold
*
*/
public abstract class AbstractWorkaround implements Workaround {
public abstract class AbstractWorkaround implements IWorkaround {
private final String id;
private final Workaround parent;
private int useCount = 0;
public AbstractWorkaround(String id) {
this(id, null); // No parent.
}
private final AcceptDenyCounter allTimeCounter;
/**
*
* @param id
* @param parent
* For (global) count: parent.use() is called from within
* this.use(), but the result is not evaluate.
*/
public AbstractWorkaround(String id, Workaround parent) {
public AbstractWorkaround(String id) {
this.id = id;
this.parent = parent;
}
public Workaround getParent() {
return parent;
this.allTimeCounter = new AcceptDenyCounter();
}
@Override
@ -38,25 +29,65 @@ public abstract class AbstractWorkaround implements Workaround {
return id;
}
@Override
public int getUseCount() {
return useCount;
}
/**
* Not meant to be overridden (alters counter, depending on result).
* Override testUse(false) instead.
*/
@Override
public boolean use() {
if (canUse()) {
useCount ++;
if (parent != null) {
// TODO: Might consider a hierarchy (parent could overrule the result to false).
parent.use();
}
if (testUse(true)) {
allTimeCounter.accept();
return true;
}
else {
// DenyUseCound is handled in sub-classes, if needed.
allTimeCounter.deny();
return false;
}
}
/**
* Not meant to be overridden. Override testUse(true) instead.
*/
@Override
public boolean canUse() {
return testUse(false);
}
@Override
public IAcceptDenyCounter getAllTimeCounter() {
return allTimeCounter;
}
/**
* Override to check for pre/side-conditions.
*
* @param isUse
* If set to false, this must not alter any data, nor have any
* other side conditions. If set to true, this is called from
* within use(), meant to update stage counters and similar. Note
* that the allTimeCounter is updated from within use, depending
* on the result of calling testUse(true).
* @return
*/
public abstract boolean testUse(boolean isUse);
/**
* Override the parent counters in instance with the ones set in this
* instance, if possible.
*
* @param instance
* @return The given instance.
*/
<W extends IWorkaround> W setParentCounters(final W instance) {
final IAcceptDenyCounter allTimeParent = this.allTimeCounter.getParentCounter();
if (allTimeParent != null) {
final IAcceptDenyCounter instanceAllTime = instance.getAllTimeCounter();
if ((instance instanceof ICounterWithParent)) {
((ICounterWithParent) instanceAllTime).setParentCounter(allTimeParent);
}
}
return instance;
}
}

View File

@ -0,0 +1,29 @@
package fr.neatmonster.nocheatplus.workaround;
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.IAcceptDenyCounter;
/**
* Workaround allowing for resetting pre/side-conditions, with an extra counter
* lasting until reset. Parent counters may not be supported.
*
* @author asofold
*
*/
public interface IStagedWorkaround {
// TODO: resetConditions: Consider to have it return false by default (x.use() || x.resetConditions() with pre-conditions).
/**
* Generic reset to the initial conditions. This does not reset the all time
* counters, other effects depend on the implementation.
*/
public void resetConditions();
/**
* Get the counter that will reset with the next call to resetConditions.
* Parent counters may not be supported.
*
* @return
*/
public IAcceptDenyCounter getStageCounter();
}

View File

@ -0,0 +1,64 @@
package fr.neatmonster.nocheatplus.workaround;
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.IAcceptDenyCounter;
/**
* Provide a means of controlling when workarounds should be able to apply,
* enabling preconditions, mid-to-long-term side-conditions, as well as
* statistics for how often a workaround has been used.
* <hr>
* The method use() must be called after all other preconditions have been met
* for that (stage of) workaround, so that success means that the (stage of)
* workaround does apply. The method canUse can be used to test if the
* workaround would apply, aimed at cases where that is better performance-wise.
*
* @author asofold
*
*/
public interface IWorkaround {
// TODO: Add setEnabled() ? Allow to configure workarounds (-> IConfigurableThing).
public String getId();
/**
* Attempt to use the workaround, considering all preconditions and
* side-conditions set. This will increase the accept counter in case of
* returning true, or increase the deny counter in case of returning false.
* It might also alter/use other counters based on the value to be returned
* (stage counter, parent counter, other side conditions).
*
* @return If actually can be used.
*/
public boolean use();
/**
* Test if this (stage of) workaround would apply. This must not have any
* side effect nor change any data. Intended use is a fast-denial check, to
* be performed before other heavier to check pre-conditions are tested, as
* well as being called from within use().
*
* @return If a call returns false, use() must also return false (until
* conditions are changed). If a call returns true, future
* implementations might still return false on use().
*/
public boolean canUse();
/**
* Retrieve the counter used for acceptance and denial with calling use().
* This counter may or may not have a parent counter.
*
* @return
*/
public IAcceptDenyCounter getAllTimeCounter();
/**
* Serving as factory, retrieve a new instance of the same kind, in the
* default state (not clone). Counters should be new counter instances,
* except for parents.
*
* @return
*/
public IWorkaround getNewInstance();
}

View File

@ -2,11 +2,15 @@ package fr.neatmonster.nocheatplus.workaround;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.AcceptDenyCounter;
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.IAcceptDenyCounter;
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.ICounterWithParent;
/**
* Simple registry for workarounds. No thread-safety built in.
*
@ -16,10 +20,10 @@ import java.util.Map;
public class SimpleWorkaroundRegistry implements WorkaroundRegistry {
/** Global counter by id. */
private final Map<String, WorkaroundCounter> counters = new HashMap<String, WorkaroundCounter>();
private final Map<String, IAcceptDenyCounter> counters = new HashMap<String, IAcceptDenyCounter>();
/** Workaround blue print by id. */
private final Map<String, Workaround> bluePrints = new HashMap<String, Workaround>();
private final Map<String, IWorkaround> bluePrints = new HashMap<String, IWorkaround>();
/** Map group id to array of workaround ids. */
private final Map<String, String[]> groups = new HashMap<String, String[]>();
@ -31,10 +35,19 @@ public class SimpleWorkaroundRegistry implements WorkaroundRegistry {
private final Map<String, String[]> workaroundSetGroups = new HashMap<String, String[]>();
@Override
public void setWorkaroundBluePrint(final Workaround... bluePrints) {
public void setWorkaroundBluePrint(final IWorkaround... bluePrints) {
// TODO: Might consistency check, plus policy for overriding (ignore all if present).
for (int i = 0; i < bluePrints.length; i++) {
final Workaround workaround = bluePrints[i];
this.bluePrints.put(workaround.getId(), workaround.getNewInstance());
final IWorkaround bluePrintCopy = bluePrints[i].getNewInstance();
this.bluePrints.put(bluePrintCopy.getId(), bluePrintCopy);
// Set a parent counter, if not already set.
final IAcceptDenyCounter allTimeCounter = bluePrintCopy.getAllTimeCounter();
if (allTimeCounter instanceof ICounterWithParent) {
final ICounterWithParent bluePrintCopyWithParent = (ICounterWithParent) bluePrintCopy;
if (bluePrintCopyWithParent.getParentCounter() == null) {
bluePrintCopyWithParent.setParentCounter(createGlobalCounter(bluePrintCopy.getId()));
}
}
}
}
@ -44,10 +57,10 @@ public class SimpleWorkaroundRegistry implements WorkaroundRegistry {
}
@Override
public void setWorkaroundSet(final String workaroundSetId, final Collection<Workaround> bluePrints, final String... groupIds) {
public void setWorkaroundSet(final String workaroundSetId, final Collection<IWorkaround> bluePrints, final String... groupIds) {
final String[] ids = new String[bluePrints.size()];
int i = 0;
for (final Workaround bluePrint : bluePrints) {
for (final IWorkaround bluePrint : bluePrints) {
final String id = bluePrint.getId();
if (!this.bluePrints.containsKey(id)) {
// Lazily register.
@ -69,9 +82,9 @@ public class SimpleWorkaroundRegistry implements WorkaroundRegistry {
@Override
public void setWorkaroundSetByIds(final String workaroundSetId, final Collection<String> bluePrintIds, final String... groupIds) {
final List<Workaround> bluePrints = new ArrayList<Workaround>(bluePrintIds.size());
final List<IWorkaround> bluePrints = new ArrayList<IWorkaround>(bluePrintIds.size());
for (final String id : bluePrintIds) {
final Workaround bluePrint = this.bluePrints.get(id);
final IWorkaround bluePrint = this.bluePrints.get(id);
if (bluePrint == null) {
throw new IllegalArgumentException("the blueprint is not registered: " + id);
}
@ -86,7 +99,7 @@ public class SimpleWorkaroundRegistry implements WorkaroundRegistry {
if (workaroundIds == null) {
throw new IllegalArgumentException("WorkaroundSet not registered: " + workaroundSetId);
}
final Workaround[] bluePrints = new Workaround[workaroundIds.length];
final IWorkaround[] bluePrints = new IWorkaround[workaroundIds.length];
for (int i = 0; i < workaroundIds.length; i++) {
bluePrints[i] = this.bluePrints.get(workaroundIds[i]);
}
@ -106,15 +119,15 @@ public class SimpleWorkaroundRegistry implements WorkaroundRegistry {
}
@Override
public WorkaroundCounter getGlobalCounter(final String id) {
public IAcceptDenyCounter getGlobalCounter(final String id) {
return counters.get(id);
}
@Override
public WorkaroundCounter createGlobalCounter(final String id) {
WorkaroundCounter counter = counters.get(id);
public IAcceptDenyCounter createGlobalCounter(final String id) {
IAcceptDenyCounter counter = counters.get(id);
if (counter == null) {
counter = new WorkaroundCounter(id);
counter = new AcceptDenyCounter();
counters.put(id, counter);
}
return counter;
@ -122,8 +135,8 @@ public class SimpleWorkaroundRegistry implements WorkaroundRegistry {
@SuppressWarnings("unchecked")
@Override
public <C extends Workaround> C getWorkaround(final String id, final Class<C> workaroundClass) {
final Workaround workaround = getWorkaround(id);
public <C extends IWorkaround> C getWorkaround(final String id, final Class<C> workaroundClass) {
final IWorkaround workaround = getWorkaround(id);
if (workaroundClass.isAssignableFrom(workaround.getClass())) {
return (C) workaround;
}
@ -133,8 +146,8 @@ public class SimpleWorkaroundRegistry implements WorkaroundRegistry {
}
@Override
public Workaround getWorkaround(final String id) {
final Workaround bluePrint = bluePrints.get(id);
public IWorkaround getWorkaround(final String id) {
final IWorkaround bluePrint = bluePrints.get(id);
if (bluePrint == null) {
throw new IllegalArgumentException("Id not registered as blueprint: " + id);
}
@ -142,12 +155,8 @@ public class SimpleWorkaroundRegistry implements WorkaroundRegistry {
}
@Override
public Map<String, Integer> getGlobalUseCount() {
final Map<String, Integer> currentCounts = new LinkedHashMap<String, Integer>(counters.size());
for (final WorkaroundCounter counter : counters.values()) {
currentCounts.put(counter.getId(), counter.getUseCount());
}
return currentCounts;
public Map<String, IAcceptDenyCounter> getGlobalCounters() {
return Collections.unmodifiableMap(counters);
}
}

View File

@ -1,60 +0,0 @@
package fr.neatmonster.nocheatplus.workaround;
/**
* Provide a means of controlling when workarounds should be able to apply,
* enabling preconditions, mid-to-long-term side-conditions, as well as
* statistics for how often a workaround has been used.
* <hr>
* The method use() must be called after all other preconditions have been met
* for that (stage of) workaround, so that success means that the (stage of)
* workaround does apply. The method canUse can be used to test if the
* workaround would apply, aimed at cases where that is better performance-wise.
*
* @author asofold
*
*/
public interface Workaround {
// TODO: getDiscardCount() ?
// TODO: Add setEnabled() ? Allow to configure workarounds.
public String getId();
/** The all-time use count. */
public int getUseCount();
/**
* Attempt to use the workaround, considering all preconditions and
* side-conditions set. This will increase the use count in case of
* returning true, it might also alter/use other counters based on the value
* to be returned.
*
* @return If actually can be used.
*/
public boolean use();
/**
* Test if this (stage of) workaround would apply, excluding checking for
* parent state. This must not have any side effect nor change any data.
* This should also not call parent.canUse, as that would lead to quadratic
* time checking with a deeper hierarchy (likely it's just 1 deep).
*
* @return
*/
public boolean canUse();
/**
* Generic reset to the initial conditions. This does not reset the use
* count, other effects depend on the implementation.
*/
public void resetConditions();
/**
* Serving as factory, retrieve a new instance of the same kind, in the
* default state (not clone).
*
* @return
*/
public Workaround getNewInstance();
}

View File

@ -1,52 +1,86 @@
package fr.neatmonster.nocheatplus.workaround;
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.AcceptDenyCounter;
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.IAcceptDenyCounter;
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.IResettableAcceptDenyCounter;
/**
* Count down to 0, use is only possible if the currentCounter is greater than
* 0. An initialCounter of 0 together with setting the currentCount manually,
* allows to activate a workaround on base of a precondition.
* allows to activate a workaround on base of a precondition. Also keep track of
* denyUseCount (all time + stage). Parent counters are not supported for the
* stage counter.
*
* @author asofold
*
*/
public class WorkaroundCountDown extends AbstractWorkaround {
public class WorkaroundCountDown extends AbstractWorkaround implements IStagedWorkaround {
/** The start value for the count down. */
private final int initialCount;
/** The current state */
private int currentCount;
private final IResettableAcceptDenyCounter stageCounter;
public WorkaroundCountDown(String id, int initialCount, Workaround parent) {
super(id, parent);
/**
*
* @param id
* @param initialCount
*/
public WorkaroundCountDown(String id, int initialCount) {
super(id);
this.initialCount = initialCount;
this.currentCount = initialCount;
this.stageCounter = new AcceptDenyCounter();
}
/**
* Set the current count down value.
* @param currentCount
*/
public void setCurrentCount(int currentCount) {
this.currentCount = currentCount;
}
/**
* Get the current count down value.
*/
public int getCurrentCount() {
return currentCount;
}
@Override
public boolean use() {
// Adjust currentCount based on super.use().
if (super.use()) {
currentCount --;
stageCounter.accept();
return true;
} else {
stageCounter.deny();
return false;
}
}
@Override
public boolean canUse() {
return currentCount > 0;
}
@Override
public void resetConditions() {
currentCount = initialCount;
stageCounter.resetCounter();
}
@Override
public WorkaroundCountDown getNewInstance() {
return new WorkaroundCountDown(getId(), initialCount, getParent());
return setParentCounters(new WorkaroundCountDown(getId(), initialCount));
}
@Override
public IAcceptDenyCounter getStageCounter() {
return stageCounter;
}
@Override
public boolean testUse(boolean isUse) {
return currentCount > 0;
}
}

View File

@ -7,28 +7,19 @@ package fr.neatmonster.nocheatplus.workaround;
*/
public class WorkaroundCounter extends AbstractWorkaround {
public WorkaroundCounter(String id, Workaround parent) {
super(id, parent);
}
public WorkaroundCounter(String id) {
super(id);
}
@Override
public boolean canUse() {
public boolean testUse(final boolean isUse) {
// Just counting.
return true;
}
@Override
public void resetConditions() {
// Nothing to do.
}
@Override
public WorkaroundCounter getNewInstance() {
return new WorkaroundCounter(getId());
return setParentCounters(new WorkaroundCounter(getId()));
}
}

View File

@ -4,10 +4,11 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.IAcceptDenyCounter;
/**
* An access point for fetching global WorkaroundCounter instances and a factory
* for fetching new sets of per-player workarounds.
@ -21,7 +22,8 @@ public interface WorkaroundRegistry {
* Convenience to retrieve any type of per-player Workaround by id, for the
* case one doesn't want to store the registry and/or individual Workaround
* implementations as members. Groups allow resetting certain types of
* workarounds in bunches.
* workarounds in bunches. The resetConditions methods only call
* resetConditions for instances of IStagedWorkaround.
*
* @author asofold
*
@ -30,16 +32,20 @@ public interface WorkaroundRegistry {
// TODO: getUseCount()
// TODO: A list for ids of just used workarounds (reset externally. Add use(id) vs alter Workaround)?
// TODO: Better optimized constructor.
// TODO: Better optimized constructor (instanceof-decisions can be pre-cached).
/** Map workaround id to workaround. */
private final Map<String, Workaround> workaroundsById = new LinkedHashMap<String, Workaround>();
private final Map<String, IWorkaround> workaroundsById = new LinkedHashMap<String, IWorkaround>();
/** Only the workarounds that might need resetting. */
private final Workaround[] mightNeedReset;
private final IStagedWorkaround[] stagedWorkarounds;
/** Map groupId to workarounds. */
private final Map<String, Workaround[]> groups;
// TODO: Consider to make accessible (flexible log/stats command) or remove keeping entire groups.
/** Map groupId to workarounds. Set to null, if no groups are present. */
private final Map<String, IWorkaround[]> groups;
/** Only the staged workarounds within a group by group id. Set to null, if no groups are present. */
private final Map<String, IStagedWorkaround[]> stagedGroups;
/**
*
@ -49,36 +55,48 @@ public interface WorkaroundRegistry {
* are set. All referenced workaround ids must be registered,
* workarounds can be in multiple groups.
*/
public WorkaroundSet(final Workaround[] bluePrints, final Map<String, String[]> groups) {
final Class<?> excludeFromReset = WorkaroundCounter.class;
final List<Workaround> mightNeedReset = new ArrayList<Workaround>(bluePrints.length);
public WorkaroundSet(final IWorkaround[] bluePrints, final Map<String, String[]> groups) {
// Add new instances to workaroundsById and stagedWorkarounds.
final ArrayList<IWorkaround> stagedWorkarounds = new ArrayList<IWorkaround>(bluePrints.length);
for (int i = 0; i < bluePrints.length; i++) {
final Workaround workaround = bluePrints[i].getNewInstance();
final IWorkaround workaround = bluePrints[i].getNewInstance();
workaroundsById.put(workaround.getId(), workaround);
if (workaround.getClass() != excludeFromReset) {
mightNeedReset.add(workaround);
if (workaround instanceof IStagedWorkaround) {
stagedWorkarounds.add(workaround);
}
}
this.mightNeedReset = mightNeedReset.toArray(new Workaround[mightNeedReset.size()]);
this.stagedWorkarounds = stagedWorkarounds.toArray(new IStagedWorkaround[stagedWorkarounds.size()]);
// Prepare fast to reset lists, if groups are given.
if (groups != null) {
this.groups = new HashMap<String, Workaround[]>();
this.groups = new HashMap<String, IWorkaround[]>();
this.stagedGroups = new HashMap<String, IStagedWorkaround[]>();
for (final Entry<String, String[]> entry : groups.entrySet()) {
final String groupId = entry.getKey();
final String[] workaroundIds = entry.getValue();
final Workaround[] group = new Workaround[workaroundIds.length];
final IWorkaround[] group = new IWorkaround[workaroundIds.length];
final ArrayList<IStagedWorkaround> stagedGroup = new ArrayList<IStagedWorkaround>(workaroundIds.length);
for (int i = 0; i < workaroundIds.length; i++) {
group[i] = getWorkaround(workaroundIds[i]);
final IWorkaround workaround = getWorkaround(workaroundIds[i]);
group[i] = workaround;
if (workaround instanceof IStagedWorkaround) {
stagedGroup.add((IStagedWorkaround) workaround);
}
}
this.groups.put(groupId, group);
if (!stagedGroup.isEmpty()) {
this.stagedGroups.put(groupId, stagedGroup.toArray(new IStagedWorkaround[stagedGroup.size()]));
}
this.groups.put(entry.getKey(), group);
}
} else {
this.groups = null;
this.stagedGroups = null;
}
}
@SuppressWarnings("unchecked")
public <C extends Workaround> C getWorkaround(final String id, final Class<C> workaroundClass) {
final Workaround present = getWorkaround(id);
public <C extends IWorkaround> C getWorkaround(final String id, final Class<C> workaroundClass) {
final IWorkaround present = getWorkaround(id);
if (!workaroundClass.isAssignableFrom(present.getClass())) {
throw new IllegalArgumentException("Wrong type of registered workaround requested: " + workaroundClass.getName() + " instead of " + present.getClass().getName());
} else {
@ -86,8 +104,8 @@ public interface WorkaroundRegistry {
}
}
public Workaround getWorkaround(final String id) {
final Workaround present = workaroundsById.get(id);
public IWorkaround getWorkaround(final String id) {
final IWorkaround present = workaroundsById.get(id);
if (present == null) {
throw new IllegalArgumentException("Workaround id not registered: " + id);
}
@ -99,8 +117,8 @@ public interface WorkaroundRegistry {
* WorkaroundCounter instances (sub classes get reset too).
*/
public void resetConditions() {
for (int i = 0; i < mightNeedReset.length; i++) {
mightNeedReset[i].resetConditions();
for (int i = 0; i < stagedWorkarounds.length; i++) {
stagedWorkarounds[i].resetConditions();
}
}
@ -111,26 +129,54 @@ public interface WorkaroundRegistry {
* @param groupId
*/
public void resetConditions(final String groupId) {
final Workaround[] workarounds = groups.get(groupId);
if (workarounds == null) {
throw new IllegalArgumentException("Group not registered: " + groupId);
}
for (int i = 0; i < workarounds.length; i++) {
workarounds[i].resetConditions();
if (this.stagedGroups != null) {
final IStagedWorkaround[] workarounds = stagedGroups.get(groupId);
if (workarounds != null) {
for (int i = 0; i < workarounds.length; i++) {
workarounds[i].resetConditions();
}
}
}
}
/**
* Unchecked use.
*
* @param workaroundId
* @return The result of IWorkaround.use() for the registered instance.
* @throws NullPointerException
* if no workaround is registered for this id.
*
*/
public boolean use(String workaroundId) {
// TODO: For consistency might throw the same exception everywhere (IllegalArgument?).
return workaroundsById.get(workaroundId).use();
}
/**
* Unchecked canUse.
*
* @param workaroundId
* @return The result of IWorkaround.canUse() for the registered
* instance.
* @throws NullPointerException
* if no workaround is registered for this id.
*
*/
public boolean canUse(String workaroundId) {
// TODO: For consistency might throw the same exception everywhere (IllegalArgument?).
return workaroundsById.get(workaroundId).canUse();
}
}
// TODO: Might make getWorkaround non public, to favor use of WorkaroundSet.
/**
* Registers workaround.getNewInstance() for the set id. Set parent to
* createGlobalCounter(id), if a global counter is desired.
* Registers workaround.getNewInstance() for the set id as a blueprint. A
* parent counter will be created, if it doesn't exist yet.
*
* @param bluePrints
*/
public void setWorkaroundBluePrint(Workaround...bluePrints);
public void setWorkaroundBluePrint(IWorkaround...bluePrints);
/**
* Specify what workaround ids belong to a certain group. Workarounds can be
@ -147,12 +193,12 @@ public interface WorkaroundRegistry {
*
* @param workaroundSetId
* @param bluePrints
* Lazily registers, if no blueprint is present. Already
* Lazily registers, if no blueprint is present (calling setWorkaroundBluePrint, note parent counters). Already
* registered blueprints are kept.
* @param groupIds
* Must already be registered.
*/
public void setWorkaroundSet(String workaroundSetId, Collection<Workaround> bluePrints, String... groupIds);
public void setWorkaroundSet(String workaroundSetId, Collection<IWorkaround> bluePrints, String... groupIds);
/**
* Define which workarounds and which groups belong to the WorkaroundSet of
@ -174,21 +220,22 @@ public interface WorkaroundRegistry {
public WorkaroundSet getWorkaroundSet(String workaroundSetId);
/**
* Get a registered global WorkaroundCounter, if registered.
* Get a registered global IAcceptDenyCounter instance, if registered.
*
* @param id
* @return The registered WorkaroundCounter instance, or null if none is
* @return The registered IAcceptDenyCounter instance, or null if none is
* registered for the given id.
*/
public WorkaroundCounter getGlobalCounter(String id);
public IAcceptDenyCounter getGlobalCounter(String id);
/**
* Get a registered global WorkaroundCounter, create if not present.
* Get a registered global IAcceptDenyCounter instance, create if not
* present.
*
* @param id
* @return
*/
public WorkaroundCounter createGlobalCounter(String id);
public IAcceptDenyCounter createGlobalCounter(String id);
/**
* Retrieve a new instance, ready for use, attached to a global counter of
@ -202,7 +249,7 @@ public interface WorkaroundRegistry {
* @throws IllegalArgumentException
* If either of id or workaroundClass is not possible to use.
*/
public <C extends Workaround> C getWorkaround(String id, Class<C> workaroundClass);
public <C extends IWorkaround> C getWorkaround(String id, Class<C> workaroundClass);
/**
* Retrieve a new instance, ready for use, attached to a global counter of
@ -213,13 +260,14 @@ public interface WorkaroundRegistry {
* @throws IllegalArgumentException
* If either of id or workaroundClass is not possible to use.
*/
public Workaround getWorkaround(String id);
public IWorkaround getWorkaround(String id);
/**
* Get all global count values by id.
* Retrieve an unmodifiable map for all registered global counters. The counters
* are not copies, so they could be altered, discouraged though.
*
* @return
*/
public Map<String, Integer> getGlobalUseCount();
public Map<String, IAcceptDenyCounter> getGlobalCounters();
}

View File

@ -0,0 +1,60 @@
package fr.neatmonster.nocheatplus.test;
import static org.junit.Assert.*;
import org.junit.Test;
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.AcceptDenyCounter;
import fr.neatmonster.nocheatplus.utilities.ds.count.acceptdeny.IAcceptDenyCounter;
public class TestAcceptDenyCounters {
/**
* Test the default implementation.
*/
@Test
public void testAcceptDenyCounter() {
AcceptDenyCounter leaf = new AcceptDenyCounter();
AcceptDenyCounter parent = new AcceptDenyCounter();
leaf.setParentCounter(parent);
// Add to leaf.
for (int i = 0; i < 73; i++) {
leaf.accept();
}
checkCounts(leaf, 73, 0, "leaf");
for (int i = 0; i < 65; i++) {
leaf.deny();
}
checkCounts(leaf, 73, 65, "leaf");
// Add only to parent.
for (int i = 0; i < 52; i++) {
parent.accept();
}
checkCounts(parent, 73 + 52, 65, "parent");
checkCounts(leaf, 73, 65, "leaf");
for (int i = 0; i < 97; i++) {
parent.deny();
}
checkCounts(parent, 73 + 52, 65 + 97, "parent");
checkCounts(leaf, 73, 65, "leaf");
// Reset parent.
parent.resetCounter();
checkCounts(parent, 0, 0, "parent");
checkCounts(leaf, 73, 65, "leaf");
}
public void checkCounts(IAcceptDenyCounter counter, int acceptCount, int denyCount, String counterName) {
if (counter.getAcceptCount() != acceptCount) {
fail("Wrong accept count for counter '" + counterName + "': " + counter.getAcceptCount() + " instead of " + acceptCount + ".");
}
if (counter.getDenyCount() != denyCount) {
fail("Wrong deny count for counter '" + counterName + "': " + counter.getDenyCount() + " instead of " + denyCount + ".");
}
}
}