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/> <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}