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}