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}