001package daikon.inv.unary.stringsequence;
002
003import daikon.PptSlice;
004import daikon.inv.DiscardInfo;
005import daikon.inv.Invariant;
006import daikon.inv.InvariantStatus;
007import daikon.inv.OutputFormat;
008import java.util.List;
009import java.util.StringJoiner;
010import org.checkerframework.checker.interning.qual.Interned;
011import org.checkerframework.checker.lock.qual.GuardSatisfied;
012import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
013import org.checkerframework.checker.nullness.qual.NonNull;
014import org.checkerframework.checker.nullness.qual.Nullable;
015import org.checkerframework.dataflow.qual.Pure;
016import org.checkerframework.dataflow.qual.SideEffectFree;
017import org.plumelib.util.ArraysPlume;
018import org.plumelib.util.Intern;
019import typequals.prototype.qual.NonPrototype;
020import typequals.prototype.qual.Prototype;
021
022// TODO: Why isn't this generated from CommonSequence.java.jpp?
023/**
024 * Represents string sequences that contain a common subset. Prints as {@code {s1, s2, s3, ...}
025 * subset of x[]}.
026 */
027public class CommonStringSequence extends SingleStringSequence {
028  static final long serialVersionUID = 20030822L;
029
030  // Variables starting with dkconfig_ should only be set via the
031  // daikon.config.Configuration interface.
032  /** Boolean. True iff CommonStringSequence invariants should be considered. */
033  public static boolean dkconfig_enabled = false;
034
035  /** The number of samples observed. */
036  private int count = 0;
037
038  /** Null means no samples have been seen yet. Empty array means intersection is empty. */
039  private @Interned String @MonotonicNonNull @Interned [] intersect = null;
040
041  protected CommonStringSequence(PptSlice ppt) {
042    super(ppt);
043  }
044
045  protected @Prototype CommonStringSequence() {
046    super();
047  }
048
049  private static @Prototype CommonStringSequence proto = new @Prototype CommonStringSequence();
050
051  /** Returns the prototype invariant for CommonStringSequence. */
052  public static @Prototype CommonStringSequence get_proto() {
053    return proto;
054  }
055
056  @Override
057  public boolean enabled() {
058    return dkconfig_enabled;
059  }
060
061  @Override
062  protected CommonStringSequence instantiate_dyn(
063      @Prototype CommonStringSequence this, PptSlice slice) {
064    return new CommonStringSequence(slice);
065  }
066
067  // Don't define clone (use the Object version instead), because this.intersect is read-only.
068  // protected Object clone();
069
070  @Override
071  public String repr(@GuardSatisfied CommonStringSequence this) {
072    return "CommonStringSequence " + varNames() + ": count=\"" + count;
073  }
074
075  private String printIntersect(@GuardSatisfied CommonStringSequence this) {
076    if (intersect == null) {
077      return "{}";
078    }
079
080    StringJoiner result = new StringJoiner(", ", "{", "}");
081    for (int i = 0; i < intersect.length; i++) {
082      result.add(intersect[i]);
083    }
084    return result.toString();
085  }
086
087  @SideEffectFree
088  @Override
089  public String format_using(@GuardSatisfied CommonStringSequence this, OutputFormat format) {
090    if (format == OutputFormat.DAIKON) {
091      return format_daikon();
092    }
093    if (format == OutputFormat.CSHARPCONTRACT) {
094      return format_csharp_contract();
095    }
096
097    return format_unimplemented(format);
098  }
099
100  public String format_daikon(@GuardSatisfied CommonStringSequence this) {
101    return printIntersect() + " subset of " + var().name();
102  }
103
104  public String format_csharp_contract(@GuardSatisfied CommonStringSequence this) {
105    if (intersect == null) {
106      return "()";
107    }
108
109    if (intersect.length == 1) {
110      return var().csharp_name() + ".Contains(" + intersect[0] + ")";
111    }
112
113    StringJoiner exp = new StringJoiner(", ", "{", "}");
114    for (String i : intersect) {
115      exp.add(i);
116    }
117
118    return "Contract.ForAll(new[] " + exp + " , x => " + var().csharp_name() + ".Contains(x))";
119  }
120
121  @Override
122  public InvariantStatus check_modified(@Interned String @Interned [] a, int count) {
123    if (a == null) {
124      return InvariantStatus.FALSIFIED;
125    } else if (intersect == null) {
126      return InvariantStatus.NO_CHANGE;
127    } else {
128      String[] tmp = new String[intersect.length];
129      int size = 0;
130      for (int i = 1; i < a.length; i++) {
131        if ((ArraysPlume.indexOf(intersect, a[i]) != -1)
132            && ((size == 0)
133                || (ArraysPlume.indexOf(ArraysPlume.subarray(tmp, 0, size), a[i]) == -1)))
134          tmp[size++] = a[i];
135      }
136
137      if (size == 0) {
138        return InvariantStatus.FALSIFIED;
139      }
140    }
141    return InvariantStatus.NO_CHANGE;
142  }
143
144  @Override
145  public InvariantStatus add_modified(@Interned String @Interned [] a, int count) {
146    if (a == null) {
147      return InvariantStatus.FALSIFIED;
148    } else if (intersect == null) {
149      intersect = Intern.intern(a);
150      return InvariantStatus.NO_CHANGE;
151    } else {
152      @Interned String[] tmp = new @Interned String[intersect.length];
153      int size = 0;
154      for (int i = 1; i < a.length; i++) {
155        if ((ArraysPlume.indexOf(intersect, a[i]) != -1)
156            && ((size == 0)
157                || (ArraysPlume.indexOf(ArraysPlume.subarray(tmp, 0, size), a[i]) == -1)))
158          tmp[size++] = a[i];
159      }
160
161      if (size == 0) {
162        return InvariantStatus.FALSIFIED;
163      }
164      intersect = Intern.intern(ArraysPlume.subarray(tmp, 0, size));
165    }
166
167    /// Use a lesser count, because this invariant is frequently a false positive.
168    // this.count += count;
169    this.count++;
170    return InvariantStatus.NO_CHANGE;
171  }
172
173  @Override
174  protected double computeConfidence() {
175    throw new Error("Not yet implemented");
176  }
177
178  @Pure
179  public @Nullable DiscardInfo isObviousImplied() {
180    return null;
181  }
182
183  @Pure
184  @Override
185  public boolean isSameFormula(Invariant other) {
186    assert other instanceof CommonStringSequence;
187    return true;
188  }
189
190  @Override
191  public @Nullable @NonPrototype CommonStringSequence merge(
192      @Prototype CommonStringSequence this,
193      List<@NonPrototype Invariant> invs,
194      PptSlice parent_ppt) {
195    @SuppressWarnings("nullness") // super.merge does not return null
196    @NonNull CommonStringSequence result = (CommonStringSequence) super.merge(invs, parent_ppt);
197    for (int i = 1; i < invs.size(); i++) {
198      CommonStringSequence inv = (CommonStringSequence) invs.get(i);
199      @Interned String @MonotonicNonNull @Interned [] invIntersect = inv.intersect;
200      if (invIntersect != null) {
201        InvariantStatus status = result.add_modified(invIntersect, inv.count);
202        if (status == InvariantStatus.FALSIFIED) {
203          return null;
204        }
205      }
206    }
207    return result;
208  }
209}