001package daikon.split;
002
003import daikon.Global;
004import java.util.ArrayList;
005import java.util.Arrays;
006import java.util.HashMap;
007import java.util.LinkedHashMap;
008import java.util.List;
009import java.util.StringJoiner;
010import java.util.logging.Level;
011import org.checkerframework.checker.nullness.qual.Nullable;
012
013// SplitterList maps from a program point name to an array of Splitter
014// objects that should be used when splitting that program point.
015// Invariant:  each of those splitters should be non-instantiated (each is
016// a factory, not an instantiated splitter).
017// It's a shame to have to hard-code for each program point name.
018
019public abstract class SplitterList {
020  // Variables starting with dkconfig_ should only be set via the
021  // daikon.config.Configuration interface.
022  // "@ref{}" produces a cross-reference in the printed manual.  It must
023  // *not* come at the beginning of a line, or Javadoc will get confused.
024  /**
025   * Boolean. Enables indiscriminate splitting (see Daikon manual, @ref{Indiscriminate splitting},
026   * for an explanation of this technique).
027   */
028  public static boolean dkconfig_all_splitters = true;
029
030  private static final HashMap<String, Splitter[]> ppt_splitters = new LinkedHashMap<>();
031
032  /** Associate an array of splitters with the program point pptname. */
033  public static void put(String pptname, Splitter[] splits) {
034    // for (int i = 0; i<splits.length; i++) {
035    //   assert splits[i].instantiated() == false;
036    // }
037
038    if ((Global.debugSplit != null) && Global.debugSplit.isLoggable(Level.FINE)) {
039      String[] splits_strings = new String[splits.length];
040      for (int i = 0; i < splits.length; i++) {
041        splits_strings[i] = splits[i].condition();
042      }
043      Global.debugSplit.fine(
044          "Registering splitters for " + pptname + ":" + Arrays.toString(splits_strings));
045    }
046
047    if (ppt_splitters.containsKey(pptname)) {
048      Splitter[] old = ppt_splitters.get(pptname);
049      Splitter[] new_splits = new Splitter[old.length + splits.length];
050      System.arraycopy(old, 0, new_splits, 0, old.length);
051      System.arraycopy(splits, 0, new_splits, old.length, splits.length);
052      ppt_splitters.put(pptname, new_splits);
053    } else {
054      assert !ppt_splitters.containsKey(pptname);
055      // assert ! ppt_splitters.containsKey(pptname)
056      //               : "SplitterList already contains " + pptname
057      //               + " which maps to" + lineSep + " " + Arrays.toString(get_raw(pptname))
058      //               + lineSep + " which is " + formatSplitters(get_raw(pptname));
059      ppt_splitters.put(pptname, splits);
060    }
061  }
062
063  // This is only used by the debugging output in SplitterList.put().
064  public static String formatSplitters(Splitter[] splits) {
065    if (splits == null) {
066      return "null";
067    }
068    StringJoiner sj = new StringJoiner(", ", "[", "]");
069    for (Splitter split : splits) {
070      sj.add("\"" + split.condition() + "\"");
071    }
072    return sj.toString();
073  }
074
075  public static Splitter @Nullable [] get_raw(String pptname) {
076    return ppt_splitters.get(pptname);
077  }
078
079  //   // This returns a list of all the splitters that are applicable to the
080  //   // program point named "name".  The list is constructed by looking up
081  //   // various parts of "name" in the SplitterList hashtable.
082  //
083  //   // This routine tries the name first, then the base of the name, then the
084  //   // class, then the empty string.  For instance, if the program point name is
085  //   // "Foo.bar(IZ)V:::EXIT2", then it tries, in order:
086  //   //   "Foo.bar(IZ)V:::EXIT2"
087  //   //   "Foo.bar(IZ)V"
088  //   //   "Foo.bar"
089  //   //   "Foo"
090  //   //   ""
091  //
092  //   public static Splitter[] get(String pptName) {
093  //     String pptName_ = pptName;        // debugging
094  //     Splitter[] result;
095  //     ArrayList splitterArrays = new ArrayList();
096  //     ArrayList splitters = new ArrayList();
097  //
098  //     result = get_raw(pptName);
099  //     if (result != null)
100  //       splitterArrays.addElement(result);
101  //
102  //     {
103  //       int tag_index = pptName.indexOf(FileIO.ppt_tag_separator);
104  //       if (tag_index != -1) {
105  //         pptName = pptName.substring(0, tag_index);
106  //         result = get_raw(pptName);
107  //         if (result != null)
108  //           splitterArrays.addElement(result);
109  //       }
110  //     }
111  //
112  //     int lparen_index = pptName.indexOf('(');
113  //     {
114  //       if (lparen_index != -1) {
115  //         pptName = pptName.substring(0, lparen_index);
116  //         result = get_raw(pptName);
117  //         if (result != null)
118  //           splitterArrays.addElement(result);
119  //       }
120  //     }
121  //     {
122  //       // The class pptName runs up to the last dot before any open parenthesis.
123  //       int dot_limit = (lparen_index == -1) ? pptName.length() : lparen_index;
124  //       int dot_index = pptName.lastIndexOf('.', dot_limit - 1);
125  //       if (dot_index != -1) {
126  //         pptName = pptName.substring(0, dot_index);
127  //         result = get_raw(pptName);
128  //         if (result != null)
129  //           splitterArrays.addElement(result);
130  //       }
131  //     }
132  //
133  //     // Empty string means always applicable.
134  //     result = get_raw("");
135  //     if (result != null)
136  //       splitterArrays.addElement(result);
137  //
138  //     if (splitterArrays.size() == 0) {
139  //         Global.debugSplit.fine("SplitterList.get found no splitters for " + pptName);
140  //         return null;
141  //     } else {
142  //       int counter = 0;
143  //       for (int i = 0; i < splitterArrays.size(); i++) {
144  //         Splitter[] tempsplitters = (Splitter[])splitterArrays.get(i);
145  //         for (int j = 0; j < tempsplitters.length; j++) {
146  //           splitters.addElement(tempsplitters[j]);
147  //           counter++;
148  //         }
149  //       }
150  //       Global.debugSplit.fine("SplitterList.get found " + counter + " splitters for " +
151  //                              pptName);
152  //     }
153  //     return (Splitter[])splitters.toArray(new Splitter[0]);
154  //   }
155  //////////////////////
156
157  /**
158   * Return the splitters associated with this program point name (or null). The resulting splitters
159   * are factories, not instantiated splitters.
160   *
161   * @return an array of splitters
162   */
163  public static Splitter @Nullable [] get(String pptName) {
164    List<Splitter[]> splitterArrays = new ArrayList<>();
165
166    for (String name : ppt_splitters.keySet()) {
167      // name is a ppt name, assumed to begin with "ClassName.functionName"
168      if (pptName.indexOf(name) != -1) {
169        Splitter[] result = get_raw(name);
170        if (result != null) {
171          splitterArrays.add(result);
172        }
173        // For the OBJECT program point, we want to use all the splitters.
174      } else if ((pptName.indexOf("OBJECT") != -1) && (name.indexOf("OBJECT") != -1)) {
175        for (Splitter[] sa : ppt_splitters.values()) {
176          splitterArrays.add(sa);
177        }
178      }
179    }
180
181    if (splitterArrays.size() == 0) {
182      Global.debugSplit.fine("SplitterList.get found no splitters for " + pptName);
183      return null;
184    } else {
185      List<Splitter> splitters = new ArrayList<>();
186      for (Splitter[] tempsplitters : splitterArrays) {
187        for (int j = 0; j < tempsplitters.length; j++) {
188          splitters.add(tempsplitters[j]);
189        }
190      }
191      Global.debugSplit.fine(
192          "SplitterList.get found " + splitters.size() + " splitters for " + pptName);
193      return splitters.toArray(new Splitter[0]);
194    }
195  }
196
197  /**
198   * Return all the splitters in this program, The resulting splitters are factories, not
199   * instantiated splitters.
200   *
201   * @return an array of splitters
202   */
203  public static Splitter[] get_all() {
204    List<Splitter> splitters = new ArrayList<>();
205    for (Splitter[] splitter_array : ppt_splitters.values()) {
206      for (int i = 0; i < splitter_array.length; i++) {
207        Splitter tempsplitter = splitter_array[i];
208        boolean duplicate = false;
209        // Weed out splitters with the same condition.
210        if (!splitters.isEmpty()) {
211          for (Splitter splitter : splitters) {
212            if (tempsplitter.condition().trim().equals(splitter.condition().trim())) {
213              // System.err.println(" duplicate " + tempsplitter.condition()); System.err.println();
214              duplicate = true;
215              break;
216            }
217          }
218        }
219        if (!duplicate) {
220          splitters.add(tempsplitter);
221        }
222      }
223    }
224    return splitters.toArray(new Splitter[0]);
225  }
226}