001package daikon.split;
002
003import daikon.Ppt;
004import daikon.PptTopLevel;
005import daikon.ValueTuple;
006import daikon.inv.DummyInvariant;
007import java.io.Serializable;
008import org.checkerframework.checker.initialization.qual.UnknownInitialization;
009import org.checkerframework.checker.interning.qual.UsesObjectEquals;
010import org.checkerframework.checker.nullness.qual.Nullable;
011
012/**
013 * A Splitter represents a test that can be used to separate all samples into two parts. For
014 * instance, a Splitter might represent the condition "x > 0". The Splitter is used to divide a
015 * collection of variable values into sub-sets. Invariant detection can then occur for the two
016 * subsets independently.
017 *
018 * <p>This class Splitter is the superclass for all the classes we dynamically compile; there will
019 * be one subclass for each condition that's checked. Other information about the splitting
020 * condition is kept in a SplitterObject object, which keeps reference to the corresponding
021 * Splitter. One instance of each Splitter subclass is then created for each program point at which
022 * the splitting condition is applicable.
023 */
024
025// Should not be "implements Serializable":  the classes are created on
026// demand, so the class doesn't exist when a serialized object is being
027// re-read.
028@UsesObjectEquals
029public abstract class Splitter implements Serializable {
030  static final long serialVersionUID = 20020122L;
031
032  /**
033   * Creates a splitter "factory" that should only be used for creating new copies via {@link
034   * #instantiateSplitter(Ppt)}. (That is, the result of "new Splitter()" should not do any
035   * splitting itself.) There is no need for subclasses to override this (but most will have to,
036   * since they will add their own constructors as well).
037   */
038  protected Splitter() {}
039
040  /**
041   * Creates a valid splitter than can be used for testing the condition via test(ValueTuple). The
042   * implementation should always set the "instantiated" protected field to true, if that field is
043   * present in the Splitter class.
044   */
045  public abstract Splitter instantiateSplitter(@UnknownInitialization(Ppt.class) Ppt ppt);
046
047  /** True for an instantiated (non-"factory") splitter. */
048  protected boolean instantiated = false;
049
050  /**
051   * Returns true for an instantiated (non-"factory") splitter. Clients also need to check valid().
052   */
053  public boolean instantiated() {
054    return instantiated;
055  }
056
057  /**
058   * Returns true or false according to whether this was instantiated correctly and test(ValueTuple)
059   * can be called without error. An alternate design would have {@link #instantiateSplitter(Ppt)}
060   * check this, but it's a bit easier on implementers of subclasses of Splitter for the work to be
061   * done (in just one place) by the caller.
062   */
063  public abstract boolean valid();
064
065  /**
066   * Returns true or false according to whether the values in the specified ValueTuple satisfy the
067   * condition represented by this Splitter. Requires that valid() returns true.
068   */
069  public abstract boolean test(ValueTuple vt);
070
071  // This method could be static; but don't bother making it so.
072  /** Returns the condition being tested, as a String. */
073  public abstract String condition();
074
075  /**
076   * Set up the static ('factory') DummyInvariant for this kind of splitter. This only modifies
077   * static data, but it can't be static because subclasses must override it.
078   */
079  public void makeDummyInvariantFactory(DummyInvariant inv) {}
080
081  /**
082   * Make an instance DummyInvariant for this instance of the splitter, if possible on an
083   * appropriate slice from ppt.
084   */
085  public void instantiateDummy(PptTopLevel ppt) {}
086
087  /** On an instantiated Splitter, give back an appropriate instantiated DummyInvariant. */
088  public abstract @Nullable DummyInvariant getDummyInvariant();
089}