001/*license*\
002   Codelet: Copyright (C) 2014, Jeff Epstein (aliteralmind __DASH__ github __AT__ yahoo __DOT__ com)
003
004   This software is dual-licensed under the:
005   - Lesser General Public License (LGPL) version 3.0 or, at your option, any later version;
006   - Apache Software License (ASL) version 2.0.
007
008   Either license may be applied at your discretion. More information may be found at
009   - http://en.wikipedia.org/wiki/Multi-licensing.
010
011   The text of both licenses is available in the root directory of this project, under the names "LICENSE_lgpl-3.0.txt" and "LICENSE_asl-2.0.txt". The latest copies may be downloaded at:
012   - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
013   - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
014\*license*/
015package  com.github.aliteralmind.codelet.simplesig;
016   import  com.github.xbn.number.LengthInRange;
017   import  com.github.xbn.number.NewLengthInRangeFor;
018   import  com.github.xbn.array.ArrayUtil;
019   import  com.github.xbn.io.NewTextAppenterFor;
020   import  com.github.xbn.io.TextAppenter;
021   import  com.github.xbn.lang.CrashIfObject;
022   import  com.github.xbn.lang.reflect.RTNoSuchMethodException;
023   import  com.github.xbn.util.JavaRegexes;
024   import  com.google.common.base.Joiner;
025   import  java.util.ArrayList;
026   import  java.util.Arrays;
027   import  java.util.Collections;
028   import  java.util.List;
029   import  java.util.regex.Matcher;
030   import  java.util.regex.Pattern;
031   import  static com.github.xbn.lang.XbnConstants.*;
032/**
033   <p>For matching a simple-parameter-name signatures with wildcards ({@code '*'}), indicating one or more of any parameter type.</p>
034
035 * @see  <a href="{@docRoot}/overview-summary.html#xmpl_links">Real world use in Codelet</a>
036 * @since  0.1.0
037 * @author  Copyright (C) 2014, Jeff Epstein ({@code aliteralmind __DASH__ github __AT__ yahoo __DOT__ com}), dual-licensed under the LGPL (version 3.0 or later) or the ASL (version 2.0). See source code for details. <a href="http://codelet.aliteralmind.com">{@code http://codelet.aliteralmind.com}</a>, <a href="https://github.com/aliteralmind/codelet">{@code https://github.com/aliteralmind/codelet}</a>
038 **/
039public abstract class SimpleParamSigSearchTerm  {
040   private static final Matcher IDENTIFIER_MTCHR = Pattern.compile(JavaRegexes.IDENTIFIER + "(?:\\[\\]|\\.\\.\\.)?").matcher("");
041   private final List<String> termList;
042   private final boolean hasAStar;
043   private final int idxFirstStar;
044   private final int idxLastStar;
045   private final String funcName;
046   private final String withParams;
047   private final String noParams;
048   private TextAppenter dbgAptr;
049   private TextAppenter dbgAptrDoesMatch;
050   /**
051      <p>Create a search term.</p>
052
053    * @param  search_termSig  May not be {@code null}, must contain parentheses with zero or more parameters, and may contain a function name before the open paren ({@code '('}). When provided, the function name must be a valid {@linkplain com.github.xbn.util.JavaRegexes#IDENTIFIER Java identifier}. Multiple {@linkplain #getTermList() parameters} are separated by commas, and each is either<ul>
054         <li>The {@linkplain java.lang.Class#getSimpleName() simple name} of the parameter's Java type, with optional array indicators ({@code "[]"}) or, in the last parameter only, ellipses ({@code "..."}) (examples: {@code "int"}, {@code "String"}, {@code "AClass"}), {@code "boolean[]"}, {@code "String..."}), or</li>
055         <li>An {@linkplain #hasAStar() asterisk} ({@code '*'}), indicating one or more of any type. Two asterisks in a row are not permissible.</li>
056         </ul>The final character must be the close paren ({@code ')'}). Get with {@link #appendToString(StringBuilder) appendToString}.
057    */
058   public SimpleParamSigSearchTerm(String search_termSig, Appendable debugBasics_ifNonNull, Appendable dbgDoesMatch_ifNonNull)  {
059      int openParenIdx = -1;
060      try  {
061         openParenIdx = search_termSig.indexOf("(");
062      }  catch(RuntimeException rx)  {
063         throw  CrashIfObject.nullOrReturnCause(search_termSig, "search_termSig", null, rx);
064      }
065
066      if(openParenIdx == -1)  {
067         throw  new SimpleParamSigSearchTermFormatException(search_termSig, "No open parenthesis ('(').");
068      }
069
070      funcName = search_termSig.substring(0, openParenIdx);
071
072      if(search_termSig.charAt(search_termSig.length() - 1) != ')')  {
073         throw  new SimpleParamSigSearchTermFormatException(search_termSig, "Last character not a close parenthesis: ')'.");
074      }
075
076      String commaList = search_termSig.substring((openParenIdx + 1), (search_termSig.length() - 1));
077      dbgAptr = NewTextAppenterFor.appendableUnusableIfNull(debugBasics_ifNonNull);
078      dbgAptrDoesMatch = NewTextAppenterFor.appendableUnusableIfNull(dbgDoesMatch_ifNonNull);
079
080      if(commaList.length() == 0)  {
081         termList = Collections.unmodifiableList(Arrays.asList(new String[]{}));
082         hasAStar = false;
083         noParams = "";
084         withParams = "()";
085         idxFirstStar = -1;
086         idxLastStar = -1;
087         if(dbgAptr.isUseable())  {
088            dbgAptr.appentln("SimpleParamSigSearchTerm: \"" + toString() + "\"");
089         }
090         return;
091      }
092
093      String[] params = commaList.split(",");
094      boolean prevIsStar = false;
095      boolean hasAStar2 = false;
096      int lenMinus1 = params.length - 1;
097      for(int i = 0; i < params.length; i++)  {
098         String param = params[i].trim();
099         if(param.length() == 0)  {
100            throw  new SimpleParamSigSearchTermFormatException(search_termSig, "Parameter " + i + " has no characters.");
101         }
102         if(param.equals("*"))  {
103            hasAStar2 =  true;
104            if(prevIsStar)  {
105               throw  new SimpleParamSigSearchTermFormatException(search_termSig, "Parameters " + (i - 1) + " and " + i + " are both '*'. Asterisks may not be next to each other.");
106            }
107            prevIsStar = true;
108            param = null;
109         }  else  {
110            prevIsStar = false;
111            if(!IDENTIFIER_MTCHR.reset(param).matches())  {
112               throw  new SimpleParamSigSearchTermFormatException(search_termSig, "Parameter " + i + " (\"" + param + "\") is not a legal Java identifier (with optional \"[]\" or \"...\" after it).");
113            }
114            if(param.endsWith("...")  &&  i < lenMinus1)  {
115               throw  new SimpleParamSigSearchTermFormatException(search_termSig, "Parameter " + i + " (\"" + param + "\") ends with \"...\", but is not the final parameter.");
116            }
117         }
118         params[i] = param;
119      }
120      termList = Collections.unmodifiableList(Arrays.asList(params));
121      hasAStar = hasAStar2;
122      noParams = Joiner.on(", ").useForNull("*").join(termList);
123      withParams = "(" + noParams + ")";
124
125      if(dbgAptr.isUseable())  {
126         dbgAptr.appentln("SimpleParamSigSearchTerm: \"" + toString() + "\"");
127      }
128
129      if(!hasAStar)  {
130         idxFirstStar = -1;
131         idxLastStar = -1;
132
133      }  else  {
134         int idxFirstStarTemp = -1;
135         for(int i = 0; i < getTermList().size(); i++)  {
136            if(getTermList().get(i) == null)  {
137               idxFirstStarTemp = i;
138               break;
139            }
140         }
141         idxFirstStar = idxFirstStarTemp;
142
143         int idxLastStarTemp = -1;
144         for(int i = (getTermList().size() - 1); i >= 0; i--)  {
145            if(getTermList().get(i) == null)  {
146               idxLastStarTemp = i;
147               break;
148            }
149         }
150         idxLastStar = idxLastStarTemp;
151      }
152
153   }
154   public void setDebug(Appendable debugBasics_ifNonNull, Appendable dbgDoesMatch_ifNonNull)  {
155      dbgAptr = NewTextAppenterFor.appendableUnusableIfNull(debugBasics_ifNonNull);
156      dbgAptrDoesMatch = NewTextAppenterFor.appendableUnusableIfNull(dbgDoesMatch_ifNonNull);
157   }
158   public TextAppenter getDebugAptrBasics()  {
159      return  dbgAptr;
160   }
161   public TextAppenter getDebugAptrDoesMatch()  {
162      return  dbgAptrDoesMatch;
163   }
164   /**
165      <p>The function name, or the empty string if a constructor.</p>
166    */
167   public String getMethodName()  {
168      return  funcName;
169   }
170   /**
171      <p>Immutable list of parameter types (their simple names), where asterisks are replaced with {@code null}.</p>
172
173    * @see  #SimpleParamSigSearchTerm(String, Appendable, Appendable)
174    */
175   public List<String> getTermList()  {
176      return  termList;
177   }
178   /**
179      <p>The original search-term parameter list.</p>
180
181    * @return  The original search-term list, surrounded by parentheses, with one space following each comma.
182    * @see  #getNoParams()
183    */
184   public String getWithParams()  {
185      return  withParams;
186   }
187   /**
188      <p>The original search-term parameter list, excluding the parentheses.</p>
189
190    * @see  #getWithParams()
191    */
192   public String getNoParams()  {
193      return  noParams;
194   }
195   /**
196      <p>Is there an asterisk anywhere in the parameter list?.</p>
197
198    * @see  #getTermList()
199    * @see  #SimpleParamSigSearchTerm(String, Appendable, Appendable)
200    */
201   public boolean hasAStar()  {
202      return  hasAStar;
203   }
204   /**
205      <p>Does this search-term match an actually-existing constructor/method?.</p>
206
207    * @return  <code>({@link #getFirstMatchProt(List, CrashIfZero, CrashIfMoreThanOne) getFirstMatchProt}(to_searchList, crashIf_zero, crashIf_moreThanOne) != null)</code>
208   public boolean doesMatchAnyProt(List<? extends SimpleParamNameSignature> to_searchList, CrashIfZero crashIf_zero, CrashIfMoreThanOne crashIf_moreThanOne)  {
209      return  (getFirstMatchProt(to_searchList, crashIf_zero, crashIf_moreThanOne) != null);
210   }
211    */
212   /**
213      <p>Does any constructor/method match?.</p>
214
215    * @return  <code>({@link #getFirstMatchProt(List, CrashIfZero, CrashIfMoreThanOne) getFirstMatchProt}(to_searchList, crashIf_zero, crashIf_moreThanOne) != null)</code>
216    * @see  #getAllMatchesProt(List, CrashIfZero)
217    */
218   protected boolean doesMatchAnyProt(List<? extends SimpleParamNameSignature> to_searchList)  {
219      return  (getFirstMatchProt(to_searchList, CrashIfZero.NO, CrashIfMoreThanOne.NO) != null);
220   }
221   /**
222      <p>Does exactly one constructor/method match?.</p>
223
224    * @return  <code>(matchList != null  &&  matchList.size() == 1)</code>
225      <br/>Where {@code matchList} is
226      <br/> &nbsp; &nbsp; <code>{@link #getAllMatchesProt(List, CrashIfZero) getAllMatchesProt}(to_searchList, {@link CrashIfZero}.{@link CrashIfZero#NO NO})</code>
227    * @see  #getOnlyMatchProt(List)
228    */
229   protected boolean doesMatchOnlyOneProt(List<? extends SimpleParamNameSignature> to_searchList)  {
230      List<? extends SimpleParamNameSignature> matchList = getAllMatchesProt(
231         to_searchList, CrashIfZero.NO);
232      return  (matchList != null  &&  matchList.size() == 1);
233   }
234   /**
235      <p>Get a new list of all matching constructors/methods.</p>
236
237    * @param  to_searchList  The actually-existing constructors/methods to search. May not be {@code null}.
238    * @param  crashIf_zero  If {@code com.github.xbn.lang.reflect.CrashIfZero#YES YES}, then zero matches is unacceptable. May not be {@code null}.
239    * @return  A new and mutable list containing all matching constructors/methods, or {@code null} if no messages match.
240    * @exception  RTNoSuchMethodException  If zero matches and {@code crashIf_zero} is {@code YES}.
241    * @see  #getFirstMatchProt(List, CrashIfZero, CrashIfMoreThanOne)
242    */
243   protected List<? extends SimpleParamNameSignature> getAllMatchesProt(List<? extends SimpleParamNameSignature> to_searchList, CrashIfZero crashIf_zero)  {
244      List<SimpleParamNameSignature> matches = null;
245      try  {                                                        //Divided by two
246         matches = new ArrayList<SimpleParamNameSignature>(to_searchList.size() >> 2);
247      }  catch(RuntimeException rx)  {
248         throw  CrashIfObject.nullOrReturnCause(to_searchList, "to_searchList", null, rx);
249      }
250      for(int i = 0; i < to_searchList.size(); i++)  {
251         SimpleParamNameSignature actual = to_searchList.get(i);
252         if(doesMatch(actual))  {
253            if(dbgAptr.isUseable())  {
254               dbgAptr.appentln(this + ": getAllMatchesProt: Match: " + actual);
255            }
256            matches.add(actual);
257         }
258      }
259      if(crashIf_zero.isYes()  &&  matches.size() == 0)  {
260         throw  new RTNoSuchMethodException("[search-term=\"" + toString() + "\", CrashIfZero.YES] " + to_searchList.size() + " methods/constructors in " + to_searchList.get(0).getMember().getDeclaringClass().getName() + " have the requested name, but none match:" + LINE_SEP + AllSimpleParamSignatures.toStringForAllListsInList(" - ", to_searchList, LINE_SEP));
261      }
262      return  matches;
263   }
264   /**
265      <p>Get the first and only match.</p>
266
267    * @return  <code>{@link #getFirstMatchProt(List, CrashIfZero, CrashIfMoreThanOne) getFirstMatchProt}(to_searchList, {@link CrashIfZero}.{@link CrashIfZero#YES YES}, {@link CrashIfMoreThanOne}.{@link CrashIfMoreThanOne#YES YES})</code>
268    * @see  #getAllMatchesProt(List, CrashIfZero) getAllMatchesProt
269    */
270   protected SimpleParamNameSignature getOnlyMatchProt(List<? extends SimpleParamNameSignature> to_searchList)  {
271      return  getFirstMatchProt(to_searchList, CrashIfZero.YES, CrashIfMoreThanOne.YES);
272   }
273   /**
274      <p>Get the first matching constructor/method.</p>
275
276    * @param  to_searchList  The actually-existing constructor/methods to search. May not be {@code null}.
277    * @param  crashIf_zero  If {@code com.github.xbn.lang.reflect.CrashIfZero#YES YES}, then zero matches is unacceptable. May not be {@code null}.
278    * @param  crashIf_moreThanOne  If {@code com.github.xbn.lang.reflect.CrashIfMoreThanOne#YES YES}, then more than one match is unacceptable. May not be {@code null}.
279    * @return  The first matching param-list, or {@code null} if none match.
280    * @exception  RTNoSuchMethodException  If zero matches and {@code crashIf_zero} is {@code YES}, or more than one constructor/method matches and {@code crashIf_moreThanOne} is {@code YES}
281    * @see  #doesMatchAnyProt(List) doesMatchAnyProt
282    */
283   protected SimpleParamNameSignature getFirstMatchProt(List<? extends SimpleParamNameSignature> to_searchList, CrashIfZero crashIf_zero, CrashIfMoreThanOne crashIf_moreThanOne)  {
284      SimpleParamNameSignature firstMatch = null;
285      try  {
286         for(SimpleParamNameSignature sig : to_searchList)  {
287            if(doesMatch(sig))  {
288               if(dbgAptr.isUseable())  {
289                  dbgAptr.appentln(this + ": doesMatch: Match: " + sig);
290               }
291
292               if(crashIf_moreThanOne.isNo())  {
293                  return  sig;
294               }
295
296               if(firstMatch != null)  {
297                  List<? extends SimpleParamNameSignature> matchList = getAllMatchesProt(to_searchList, crashIf_zero);
298                  throw  new RTNoSuchMethodException("[shortcut=\"" + toString() + "\", CrashIfMoreThanOne.YES] " + matchList.size() + " methods/constructors in " + firstMatch.getMember().getDeclaringClass().getName() + " match:" + LINE_SEP + AllSimpleParamSignatures.toStringForAllListsInList(" - ", matchList, LINE_SEP));
299               }
300               firstMatch = sig;
301            }
302         }
303      }  catch(RuntimeException rx)  {
304         throw  CrashIfObject.nullOrReturnCause(to_searchList, "to_searchList", null, rx);
305      }
306
307      maybeCrashIfNoneMatch((firstMatch == null), crashIf_zero, to_searchList);
308
309      return  firstMatch;
310   }
311      private void maybeCrashIfNoneMatch(boolean true_ifZero, CrashIfZero crashIf_zero, List<? extends SimpleParamNameSignature> to_searchList)  {
312         if(crashIf_zero.isYes()  &&  true_ifZero)  {
313            throw  new RTNoSuchMethodException("[shortcut=\"" + toString() + "\", CrashIfZero.YES] " + to_searchList.size() + " methods/constructors in " + to_searchList.get(0).getMember().getDeclaringClass().getName() + " have the requested name, but none match:" + LINE_SEP + AllSimpleParamSignatures.toStringForAllListsInList(" - ", to_searchList, LINE_SEP));
314         }
315      }
316   public int indexOfStar()  {
317      return  idxFirstStar;
318   }
319   public int lastIndexOfStar()  {
320      return  idxLastStar;
321   }
322   /**
323      <p>Does a constructor/method match?.</p>
324
325      <p><i>This function was a <b>beast</b>.</i></p>
326
327    * @param  actual_cnstrMthd  May not be {@code null}.
328    */
329   public boolean doesMatch(SimpleParamNameSignature actual_cnstrMthd)  {
330
331      List<String> requiredParams = getTermList();
332      List<String> actualParams = actual_cnstrMthd.getParamNameList();
333      Object[] rqdParams4DbgOnly = null;
334      Object[] actlParams4DbgOnly = null;
335
336      if(getDebugAptrDoesMatch().isUseable())  {
337         getDebugAptrDoesMatch().appentln(this + ": doesMatch?");
338         getDebugAptrDoesMatch().appentln(this + ":   - " + requiredParams.size() + " search-term params: " + toString());
339         getDebugAptrDoesMatch().appentln(this + ":   - " + actualParams.size() + " actual params: " + actual_cnstrMthd);
340      }
341
342      try  {
343         if(!actual_cnstrMthd.getMethodName().equals(getMethodName()))  {
344            if(getDebugAptrDoesMatch().isUseable())  {
345               getDebugAptrDoesMatch().appentln(this + ":    FALSE: Method names different");
346            }
347            return  false;
348         }
349      }  catch(RuntimeException rx)  {
350         throw  CrashIfObject.nullOrReturnCause(actual_cnstrMthd, "actual_cnstrMthd", null, rx);
351      }
352
353      if(getDebugAptrDoesMatch().isUseable())  {
354         rqdParams4DbgOnly = requiredParams.toArray();
355         for(int i = 0; i < rqdParams4DbgOnly.length; i++)  {
356            if(rqdParams4DbgOnly[i] == null)  {
357               rqdParams4DbgOnly[i] = "*";
358            }
359         }
360         actlParams4DbgOnly = actualParams.toArray();
361         getDebugAptrDoesMatch().appentln(this + ":    Method names equal. Analyzing parameters:");
362      }
363
364      int rqdIdx = 0;
365      int actlIdx = 0;
366
367      while(rqdIdx < requiredParams.size()  &&
368                actlIdx < actualParams.size())  {
369
370/*
371         int actlParamsToLeft = actlIdx;
372         int actlParamsRemaining = actualParams.size() - actlIdx;
373
374         LengthInRange rqdParamIdxRangeToLeft = ((indexOfStar() != -1  &&
375                  indexOfStar() < rqdIdx)
376            ?  NewLengthInRangeFor.min(null, 0, null)
377            :  new LengthInRange(0, rqdIdx));
378         int sizeMinusIdx = requiredParams.size() - rqdIdx;
379         LengthInRange rqdParamIdxRangeAtAndToRight = ((lastIndexOfStar() != -1  &&
380                  rqdIdx <= lastIndexOfStar())
381            ?  NewLengthInRangeFor.min(null, rqdIdx, null)
382            :  new LengthInRange(rqdIdx, requiredParams.size()));
383 */
384
385         if(getDebugAptrDoesMatch().isUseable())  {
386            getDebugAptrDoesMatch().appentln(this + ":       search-term" +
387               ArrayUtil.toStringHighlightElement(rqdParams4DbgOnly, "<<", rqdIdx, ">>"));
388            getDebugAptrDoesMatch().appentln(this + ":       actual:   " +
389               ArrayUtil.toStringHighlightElement(actlParams4DbgOnly, "<<", actlIdx, ">>"));
390         }
391
392         String rqdParam = requiredParams.get(rqdIdx);
393
394         if(rqdParam == null)  {
395            if(getDebugAptrDoesMatch().isUseable())  {
396               getDebugAptrDoesMatch().appentln(this + ":       Advancing both: Parameter is asterisk ('*')");
397            }
398            rqdIdx++;
399            actlIdx++;
400            continue;
401         }
402
403         //Current search-term param is not an asterisk.
404
405         int rqdSizeMinusIdx = requiredParams.size() - rqdIdx;
406         LengthInRange paramSpotsRemainingInSearchTermRange = ((lastIndexOfStar() != -1  &&
407                  lastIndexOfStar() > rqdIdx)  //Not ">=", because this param
408                                               //is definitely not an asterisk
409            ?  NewLengthInRangeFor.min(null, rqdSizeMinusIdx, null)
410            :  NewLengthInRangeFor.exactly(null, rqdSizeMinusIdx, null));
411
412         int origActlIdx = actlIdx;
413         while(actlIdx < actualParams.size()  &&
414               !paramSpotsRemainingInSearchTermRange.isIn(actualParams.size() - actlIdx))  {
415            actlIdx++;
416         }
417
418         if(origActlIdx != actlIdx)  {
419            if(getDebugAptrDoesMatch().isUseable())  {
420               getDebugAptrDoesMatch().appentln(this + ":       Advancing actual: Remaining parameter spot-count exceeds search-term.");
421            }
422            continue;
423         }
424
425         String actlParam = actualParams.get(actlIdx);
426
427         if(!rqdParam.equals(actlParam))  {
428            if(indexOfStar() != -1  &&  indexOfStar() < rqdIdx)  {
429               if(getDebugAptrDoesMatch().isUseable())  {
430                  getDebugAptrDoesMatch().appentln(this + ":       Advancing actual: Params don't match, but an asterisk exists earlier in the search-term");
431               }
432               actlIdx++;
433               continue;
434            }
435            if(getDebugAptrDoesMatch().isUseable())  {
436               getDebugAptrDoesMatch().appentln(this + ":       FALSE: Params don't match, and no asterisk exists earlier in the search-term");
437            }
438            return  false;
439         }
440
441         if(getDebugAptrDoesMatch().isUseable())  {
442            getDebugAptrDoesMatch().appentln(this + ":       Advancing both: Params match");
443         }
444         rqdIdx++;
445         actlIdx++;
446      }
447
448      if(rqdIdx == requiredParams.size())  {
449         if(actlIdx == actualParams.size())  {
450            if(getDebugAptrDoesMatch().isUseable())  {
451               getDebugAptrDoesMatch().appentln(this + ":       TRUE: Matched (all search-terms match all actual)");
452            }
453            return  true;
454         }
455
456         if(requiredParams.size() > 0  &&
457               requiredParams.get(requiredParams.size() - 1) == null)  {
458            if(getDebugAptrDoesMatch().isUseable())  {
459               getDebugAptrDoesMatch().appentln(this + ":       TRUE: At least one actual param remains, but last search-term param is asterisk");
460            }
461            return  true;
462         }
463
464         if(getDebugAptrDoesMatch().isUseable())  {
465            getDebugAptrDoesMatch().appentln(this + ":       FALSE: End of search-term reached, actual params remain");
466         }
467         return  false;
468      }
469
470      if(getDebugAptrDoesMatch().isUseable())  {
471         getDebugAptrDoesMatch().appentln(this + ":       FALSE: End of actual signature reached, search-term params remain");
472      }
473      return  false;
474   }
475      private void debugHighlightParams(Object[] rqd_params, int rqd_idx, Object[] actl_parms, int actl_idx)  {
476         if(getDebugAptrDoesMatch().isUseable())  {
477            getDebugAptrDoesMatch().appentln(this + ":       search-term: " +
478               ArrayUtil.toStringHighlightElement(rqd_params, "<<", rqd_idx, ">>"));
479            getDebugAptrDoesMatch().appentln(this + ":          Actual:   " +
480               ArrayUtil.toStringHighlightElement(actl_parms, "<<", actl_idx, ">>"));
481         }
482      }
483   /**
484    * @return  <code>{@link #appendToString(StringBuilder) appendToString}(new StringBuilder()).toString()</code>
485    */
486   public String toString()  {
487      return  appendToString(new StringBuilder()).toString();
488   }
489   /**
490      <p>The original shortcut as provided to the constructor, with a single space following each comma.</p>
491
492      <p>This appends <code>{@link #getMethodName() getMethodName}() + {@link #getWithParams() getWithParams}()</code>.</p>
493
494    * @see  #toString()
495    * @see  #SimpleParamSigSearchTerm(String, Appendable, Appendable)
496    */
497   public StringBuilder appendToString(StringBuilder to_appendTo)  {
498      try  {
499         return  to_appendTo.append(getMethodName()).append(getWithParams());
500      }  catch(RuntimeException rx)  {
501         throw  CrashIfObject.nullOrReturnCause(to_appendTo, "to_appendTo", null, rx);
502      }
503   }
504}