001/*license*\
002   Codelet
003
004   Copyright (c) 2014, Jeff Epstein (aliteralmind __DASH__ github __AT__ yahoo __DOT__ com)
005
006   This software is dual-licensed under the:
007   - Lesser General Public License (LGPL) version 3.0 or, at your option, any later version;
008   - Apache Software License (ASL) version 2.0.
009
010   Either license may be applied at your discretion. More information may be found at
011   - http://en.wikipedia.org/wiki/Multi-licensing.
012
013   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:
014   - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
015   - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
016\*license*/
017package  com.github.aliteralmind.codelet.simplesig;
018   import  com.github.xbn.array.ArrayUtil;
019   import  com.github.xbn.array.NullContainer;
020   import  com.github.xbn.io.NewTextAppenterFor;
021   import  com.github.xbn.io.TextAppenter;
022   import  com.github.xbn.lang.CrashIfObject;
023   import  com.github.xbn.lang.IllegalArgumentStateException;
024   import  com.github.xbn.lang.reflect.InvokeMethodWithRtx;
025   import  com.github.xbn.lang.reflect.ReflectRtxUtil;
026   import  com.github.xbn.regexutil.RegexUtil;
027   import  com.github.xbn.util.JavaRegexes;
028   import  java.lang.reflect.Method;
029   import  java.util.ArrayList;
030   import  java.util.Arrays;
031   import  java.util.List;
032   import  java.util.Objects;
033   import  java.util.regex.Matcher;
034   import  java.util.regex.Pattern;
035   import  static com.github.xbn.lang.XbnConstants.*;
036/**
037   <p>The pieces that represent a method, containing its return-type, containing-class, function-name, and a comma-delimited, and strictly-formatted string-list of its parameters--parameters which may only be of a primitive type or strings. This class is unrelated to {@link SimpleParamNameSignature}. Also contains utilities for creating this object from a &quot;string signature&quot; in the format:</p>
038
039   <p><code>&quot;fully.qualified.package.ClassName#functionName(\&quot;all\&quot;, true, \&quot;parameters\&quot;, (byte)-112)&quot;</code></p>
040
041   <p>There are two alternative formats, one in which the package name is not provided:</p>
042
043   <p><code>&quot;ClassName#functionName(\&quot;all\&quot;, true, \&quot;parameters\&quot;, (byte)-112)&quot;</code></p>
044
045   <p>and another in which only the function is provided:</p>
046
047   <p><code>&quot;functionName(\&quot;all\&quot;, true, \&quot;parameters\&quot;, (byte)-112)&quot;</code></p>
048
049   <p>In the two alternative signatures, default package or class values must be specified. If there are no parameters following the function name, it defaults to {@code "()"}.</p>
050
051   <h3>Example: No defaults</h3>
052
053   <p>A string signature where everything (the package, class, and function name) is provided.</p>
054
055{@.codelet.and.out com.github.aliteralmind.codelet.examples.simplesig.SimpleMethodSigNoDefaults%eliminateCommentBlocksAndPackageDecl()}
056
057   <h3>Example: Default classes</h3>
058
059   <p>This demonstrates a string signature in which the (package and) class name is not specified. The <i>potential</i> classes, one in which the function <i>must</i> exist, are provided directly.</p>
060
061{@.codelet.and.out com.github.aliteralmind.codelet.examples.simplesig.SimpleMethodSigWithClassDefaults%eliminateCommentBlocksAndPackageDecl()}
062
063 * @since  0.1.0
064 * @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>
065 **/
066public class SimpleMethodSignature  {
067   /**
068      <p>Matches a validly formatted string-signature, as required by {@link #newFromStringAndDefaults(Class, Object, String, Class[], Appendable) newFromStringAndDefaults}. View the source-code for more documentation.</p>
069
070      <p>Five named capture-groups:<ul>
071         <li><b><code>package</code>:</b> The fully-qualified package-name, ending with a dot. When non-{@code null}, this is always followed by <code>className1</code>.</li>
072         <li><b><code>className1</code>:</b> The name of the class that exists in <code>package</code>. When <code>package</code> is {@code null}, this is as well.</li>
073         <li><b><code>className2</code>:</b> The class name existing in package <code>default_package</code> (parameter in {@code newFromStringAndDefaults}). <i>When non-{@code null}, <code>default_package</code> must be {@code null}. When both this and <code>className1</code> are {@code null}</i>, <code>default_classesInOrder</code> must be non-{@code null}.</li>
074         <li><b><code>funcName</code>:</b> Always non-null.</li>
075         <li><b><code>params</code>:</b> Always non-null, but may be the empty-string.</li>
076      </ul></p>
077
078    * @see  com.github.xbn.util.JavaRegexes#IDENTIFIER JavaRegexes#IDENTIFIER
079    * @see  com.github.xbn.util.JavaRegexes#PACKAGE_NAME JavaRegexes#PACKAGE_NAME
080    */
081   public static final String STRING_SIGNATURE_REGEX = "^" +      //Line start
082         "(?:(?:" +                                               //CrashIfZero or one of the following:
083            "(?<package>" + JavaRegexes.PACKAGE_NAME + "\\.)" +   //  package-name followed by
084                    //@since  0.1.2: FIXED: SimpleMethodSignature.STRING_SIGNATURE_REGEX incorrectly ommitted the literal dot between the package and class names.
085               "(?<className1>" + JavaRegexes.IDENTIFIER + ")" +  //  class-name
086         "|" +                                                    //OR
087            "(?<className2>" + JavaRegexes.IDENTIFIER + "))#)?" + //  class-name (requires default_package)
088         "(?<funcName>" + JavaRegexes.IDENTIFIER + ")" +          //Followed by function-name
089         "\\((?<params>.*)\\)" +                                  //and all its parameters
090      "$";                                                        //Line end
091
092   private final Class<?>     returnTypeCls        ;
093   private final Class<?>[]   containsFuncClsInOrdr;
094   private final String       funcNm               ;
095   private final String       paramStrList         ;
096   private final TextAppenter dbgAptr              ;
097   /**
098        <p>YYY</p>
099
100    **/
101   public SimpleMethodSignature(Class<?> return_typeCls, Class<?>[] containsFuncClasses_inOrder, String func_name, String param_strList, Appendable debugDest_ifNonNull)  {
102      try  {
103         if(containsFuncClasses_inOrder.length == 0)  {
104            throw  new IllegalArgumentException("containsFuncClasses_inOrder has no elements.");
105         }
106      }  catch(RuntimeException rx)  {
107         throw  CrashIfObject.nullOrReturnCause(containsFuncClasses_inOrder, "containsFuncClasses_inOrder", null, rx);
108      }
109      for(int i = 0; i < containsFuncClasses_inOrder.length; i++)  {
110         if(containsFuncClasses_inOrder[i] == null)  {
111            throw  new NullPointerException("containsFuncClasses_inOrder[" + i + "]");
112         }
113      }
114      Objects.requireNonNull(return_typeCls, "return_typeCls");
115      Objects.requireNonNull(func_name, "func_name");
116      Objects.requireNonNull(param_strList, "param_strList");
117
118      returnTypeCls = return_typeCls;
119      containsFuncClsInOrdr = Arrays.copyOf(containsFuncClasses_inOrder, containsFuncClasses_inOrder.length);
120      funcNm = func_name;
121      paramStrList = param_strList;
122      dbgAptr = NewTextAppenterFor.appendableSuppressIfNull(debugDest_ifNonNull);
123   }
124   public TextAppenter getDbgAptr()  {
125      return  dbgAptr;
126   }
127   public Class<?> getReturnType()  {
128      return  returnTypeCls;
129   }
130   public List<Object> getParamValueObjectList()  {
131      return  SimpleMethodSignature.getObjectListFromCommaSepParamString(getParamStringList());
132   }
133   public Class<?> getMayContainFuncClass(int index)  {
134      try  {
135         return  containsFuncClsInOrdr[index];
136      }  catch(ArrayIndexOutOfBoundsException abx)  {
137         throw  new ArrayIndexOutOfBoundsException("index (" + index + ") is invalid given getMayContainFuncClassCount()=" + getMayContainFuncClassCount() + ".");
138      }
139   }
140
141   /**
142      <p>Execute the classes {@code main} function. This requires the {@code main} function to exist in {@link #getMayContainFuncClass(int) getMayContainFuncClass}{@code (0)}.</p>
143
144    * @see  #invokeGetMainOutputNoExtraParams(String)
145    */
146   public void invokeMainNoExtraParams() throws NoSuchMethodException  {
147      List<Object> paramValueList = getParamValueObjectList();
148      Class<?>[] paramTypes = ReflectRtxUtil.getClassArrayFromObjects(paramValueList.toArray());
149
150      Method mainFunc = getMethodFromParamTypes(paramTypes);
151
152      new InvokeMethodWithRtx(mainFunc).
153         sstatic().parameters(paramValueList.toArray()).invokeVoid();
154   }
155   /**
156      <p>Execute the classes <a href="http://docs.oracle.com/javase/tutorial/getStarted/application/index.html#MAIN">{@code main} function</a> and get its console output. This requires the {@code main} function to exist in {@link #getMayContainFuncClass(int) getMayContainFuncClass}{@code (0)}.</p>
157
158    * @see  #invokeMainNoExtraParams()
159    */
160   public String invokeGetMainOutputNoExtraParams(String app_descriptionNotClassName)  {
161      List<Object> paramValueList = getParamValueObjectList();
162//              Class<?>[] paramTypes = ReflectRtxUtil.getClassArrayFromObjects(paramValueList.toArray());
163
164      String[] stringParams = ArrayUtil.getStringArrayOrNull(paramValueList.toArray(), NullContainer.BAD, "getParamValueObjectList()");
165      return  InvokeMethodWithRtx.getApplicationOutput(getMayContainFuncClass(0),
166         stringParams, app_descriptionNotClassName);
167   }
168/*
169   public String[] getCmdLineParamsForPublicObjList()  {
170      return  ArrayUtil.getStringArrayOrNull(paramValueObjList.toArray(), false, "paramValueObjList");
171   }
172 */
173   public Method getMethod() throws NoSuchMethodException  {
174      return  getMethodFromParamTypes(null);
175   }
176   public Method getMethodFromParamValueList(List<Object> paramValues_nullForSigDefault) throws NoSuchMethodException  {
177      return  getMethodFromParamTypes(ReflectRtxUtil.getClassArrayFromObjects(paramValues_nullForSigDefault.toArray()));
178   }
179   /**
180      <p>Get the method as specified in the {@code SimpleMethodSignature}, which must exist in one of the may-contain classes.</p>
181
182    * @see  #getMayContainFuncClass(int)
183    * @see  <code><a href="http://stackoverflow.com/questions/22876120/how-to-test-if-a-private-static-function-exists-in-a-class-without-having-to-ca">http://stackoverflow.com/questions/22876120/how-to-test-if-a-private-static-function-exists-in-a-class-without-having-to-ca</a></code>
184    */
185   public Method getMethodFromParamTypes(Class<?>[] paramClasses_nullForSigDefault) throws NoSuchMethodException  {
186/*
187      List<Class<?>> paramClassList = null;
188      try  {
189         paramClassList = ((paramClasses_nullForSigDefault != null)
190         ?  paramClasses_nullForSigDefault
191         :  getClassListFromObjects(getObjectListFromCommaSepParamString(getParamStringList())));
192      }  catch(RuntimeException rx)  {
193         throw  CrashIfObject.nullOrReturnCause(paramClasses_nullForSigDefault, "paramClasses_nullForSigDefault", null, rx);
194      }
195      Class<?>[] paramClasses = null;
196      try  {
197         paramClasses = paramClasses_nullForSigDefault.toArray(new Class<?>[paramClasses_nullForSigDefault.size()]);
198      }  catch(RuntimeException rx)  {
199         throw  CrashIfObject.nullOrReturnCause(paramClasses_nullForSigDefault, "paramClasses_nullForSigDefault", null, rx);
200      }
201 */
202                if(paramClasses_nullForSigDefault == null)  {
203                paramClasses_nullForSigDefault = ReflectRtxUtil.getClassArrayFromObjects(
204            getObjectListFromCommaSepParamString(getParamStringList()).toArray());
205      }
206
207      if(getDbgAptr() != null)  {
208         getDbgAptr().appentln("SimpleMethodSignature.getMethodFromParamTypes: paramClasses_nullForSigDefault: " + ReflectRtxUtil.getClassNames(null, paramClasses_nullForSigDefault, null, ", "));
209      }
210
211      Method mthd = null;
212      for(int i = 0; i < getMayContainFuncClassCount(); i++)  {
213         try  {
214            if(getDbgAptr() != null)  {
215               getDbgAptr().appentln("   Potential containing class " + (i+1) + " of " + getMayContainFuncClassCount() + ": " + getMayContainFuncClass(i).getName() + "...");
216            }
217
218            mthd = getMayContainFuncClass(i).getDeclaredMethod(getFunctionName(), paramClasses_nullForSigDefault);
219
220            if(mthd.getReturnType() != getReturnType())  {
221               throw  new NoSuchMethodException("Method found, but with unexpected return type. getReturnType()=" + getReturnType().getName() + ", method: " + mthd);
222            }
223
224            if(getDbgAptr() != null)  {
225               getDbgAptr().appentln("   ...found");
226            }
227
228            return  mthd;
229         }  catch(NoSuchMethodException nsmx)  {
230            if(getDbgAptr() != null)  {
231               getDbgAptr().appentln("   ...method not in class: " + nsmx);
232            }
233            //Using try-catch as the false condition is hopefully temporary.
234            //See the stackoverflow question for an alternative.
235         }
236      }
237
238      throw  new NoSuchMethodException("this=" + toString() + LINE_SEP + " - paramClasses_nullForSigDefault=" + ReflectRtxUtil.getClassNames(null, paramClasses_nullForSigDefault, null, ", "));
239   }
240   public int getMayContainFuncClassCount()  {
241      return  containsFuncClsInOrdr.length;
242   }
243   public String getFunctionName()  {
244      return  funcNm;
245   }
246   public String getParamStringList()  {
247      return  paramStrList;
248   }
249   public String toString()  {
250      return  appendToString(new StringBuilder()).toString();
251   }
252   public StringBuilder appendToString(StringBuilder to_appendTo)  {
253      return  appendStringSig_ret_class_func_params(to_appendTo, true, true, true, true);
254   }
255   public String getStringSig_ret_class_func_params(boolean doInclude_returnType, boolean doInclude_class, boolean doInclude_funcName, boolean doInclude_params)  {
256      return  appendStringSig_ret_class_func_params(new StringBuilder(), doInclude_returnType, doInclude_class, doInclude_funcName, doInclude_params).toString();
257   }
258   public StringBuilder appendStringSig_ret_class_func_params(StringBuilder to_appendTo, boolean doInclude_returnType, boolean doInclude_class, boolean doInclude_funcName, boolean doInclude_params)  {
259      if(doInclude_returnType)  {
260         to_appendTo.append(getReturnType().getName()).append(" ");
261      }
262
263      if(doInclude_class)  {
264         if(containsFuncClsInOrdr.length == 1)  {
265            to_appendTo.append(containsFuncClsInOrdr[0].getName());
266         }  else  {
267            to_appendTo.append("CLASS");
268         }
269      }
270
271      if(doInclude_class  &&  doInclude_funcName)  {
272            to_appendTo.append("#");
273      }
274
275      if(doInclude_funcName)  {
276         to_appendTo.append(getFunctionName());
277      }
278      if(doInclude_params)  {
279         to_appendTo.append("(").append(getParamStringList()).append(")");
280      }
281
282      if(doInclude_class  &&  containsFuncClsInOrdr.length > 1)  {
283         to_appendTo.append(", where CLASS is one of: ").append(LINE_SEP);
284         ReflectRtxUtil.appendClassNames(to_appendTo, " - ", containsFuncClsInOrdr, null, LINE_SEP);
285      }
286/*
287      to_appendTo.append(LINE_SEP).append("paramClassList=[");
288
289         ReflectRtxUtil.appendClassNames(to_appendTo,
290            paramClassList.toArray(new Class<?>[paramClassList.size()]), ", ");
291
292      to_appendTo.append("]").append(LINE_SEP).append("paramValueObjList=[");
293
294         ReflectRtxUtil.appendClassNames(to_appendTo,
295            ReflectRtxUtil.getClassArrayFromObjects(paramValueObjList.toArray()), ", ");
296
297      to_appendTo.append("]");
298 */
299      return  to_appendTo;
300   }
301   /**
302      <p>Splits a string-signature into its parts, for the classes <a href="http://docs.oracle.com/javase/tutorial/getStarted/application/index.html#MAIN">{@code main}</a> function (or any non-returning {@code void} function), and where the fully-qualified class is explicitely specified in the string (no defaults are used).</p>
303
304    * @return  <code>{@link #newFromStringAndDefaults(Class, Object, String, Class[], Appendable) newFromStringAndDefaults}(Void.class, class_funcParamStringSignature, null, null, debugDest_ifNonNull)</code>
305    */
306
307   public static final SimpleMethodSignature getForMainFromStringSig(Object class_funcParamStringSignature, Appendable debugDest_ifNonNull) throws ClassNotFoundException, SimpleMethodSigFormatException  {
308      return  newFromStringAndDefaults(Void.class, class_funcParamStringSignature, null, null, debugDest_ifNonNull);
309   }
310   /**
311      <p>Splits a string-signature into its parts. An example string-signature is
312      <br/> &nbsp; &nbsp; {@code "ClassName#functionName(\"parameter\", \"list\", 1, (byte)-3, true)"}</p>
313
314      <h3>Format requirements</h3>
315
316      <p>{@code "functionName()"}</p>
317
318      <p>A function with no parameters that exists in the default class. In all cases, when there are <b><u>no parameters</u></b>, the empty parentheses may be omitted: {@code "functionName"}.</p>
319
320      <p>{@code "MyClass#functionName()"}</p>
321
322      <p>A function that exists in {@code MyClass}, which is in the default package.</p>
323
324      <p>{@code "com.something.me.MyClass#functionName()"}</p>
325
326      <p>A function that exists in a specific class.</p>
327
328      <p>{@code "com.something.me.MyClass#functionName(true, 1, \"hello\", ...)"}</p>
329
330      <p>A function that exists in a specific class, with a particular set of {@linkplain #getParamValueObjectList() parameters}.</p>
331
332    * @param  class_funcParamStringSignature  The string-signature. May not be null or empty, and must be formatted as specified above. Specifically, it must be matched by {@link #STRING_SIGNATURE_REGEX}.
333    * @param  default_package  The default package to use when the string-signature <i>specifies a class-name but does not specify a package</i>. When the string-signature does not contain a package, and no default class is specified, this must be non-{@code null} and non-empty, must end with a dot ({@code '.'}), and must be the package in which the specified class <i>as specified in the string-signature</i> exists ({@code default_classesInOrder} must be {@code null}). <i>The class must exist in a package</i>.
334    * @param  default_classesInOrder  The ordered set of classes to use when no class is specified in the string-signature. The function must exist in one of these classes. The search order is left-to-right (starting with element zero). When the class is specified in the string-signature, {@code default_classesInOrder} must be {@code null}. Otherwise, must be non-{@code null}, non-empty, and elements may not be {@code null}, and <i>should</i>  be unique. When non-{@code null}, {@code default_package} must be {@code null}.
335    * @return  A non-{@code null} {@code SimpleMethodSigFormatException}.
336    * @exception  ClassNotFoundException  If the class name, but not its package, is in the string-signature, and the class does not exist in the default package.
337    * @exception  SimpleMethodSigFormatException  If {@code class_funcParamStringSignature} is invalidly-formatted.
338    * @exception  IllegalArgumentStateException  If either<ul>
339         <li>There is no package specified in the string-signature and {@code default_package} is {@code null}.</li>
340         <li>There is no class name specified in the string-signature and {@code default_classesInOrder} is {@code null}.</li>
341      </ul>
342    * @see  #getForMainFromStringSig(Object, Appendable)
343    */
344   public static final SimpleMethodSignature newFromStringAndDefaults(Class<?> return_typeCls, Object class_funcParamStringSignature, String default_package, Class<?>[] default_classesInOrder, Appendable debugDest_ifNonNull) throws ClassNotFoundException  {
345      TextAppenter dbgAptr = NewTextAppenterFor.appendableSuppressIfNull(debugDest_ifNonNull);
346      if(dbgAptr != null)  {
347         dbgAptr.appentln("SimpleMethodSignature.newFromStringAndDefaults:");
348         dbgAptr.appentln(" - return_typeCls: " + return_typeCls.getName());
349         dbgAptr.appentln(" - class_funcParamStringSignature: [" + class_funcParamStringSignature + "]");
350         dbgAptr.appentln(" - default_package: [" + default_package + "]");
351         dbgAptr.appent(" - default_classesInOrder:");
352         if(default_classesInOrder != null)  {
353            dbgAptr.appentln();
354            for(int i = 0; i < default_classesInOrder.length; i++)  {
355               dbgAptr.appentln("    - " + i + ": " + default_classesInOrder[i].getName());
356            }
357         }  else  {
358            dbgAptr.appentln(" null");
359         }
360      }
361
362      try  {
363         if(!RegexUtil.resetGetMatcherCINullString(classFuncParamsMtchr, class_funcParamStringSignature.toString(), "class_funcParamStringSignature").matches())  {
364            throw  new SimpleMethodSigFormatException(class_funcParamStringSignature, "Signature not matched by [" + classFuncParamsMtchr.pattern() + "]");
365         }
366      }  catch(RuntimeException rx)  {
367         throw  CrashIfObject.nullOrReturnCause(class_funcParamStringSignature, "class_funcParamStringSignature", null, rx);
368      }
369
370      Class[] defaultClasses = null;
371
372      //Element 3: Parameters
373      String paramList = classFuncParamsMtchr.group("params");
374      String funcName = classFuncParamsMtchr.group("funcName");
375
376      String pkgNm = classFuncParamsMtchr.group("package");
377      String clsNm = classFuncParamsMtchr.group("className1");
378      if(clsNm == null)  {
379         clsNm = classFuncParamsMtchr.group("className2");
380      }
381
382      if(clsNm == null)  {
383         if(default_classesInOrder == null)  {
384            throw  new IllegalArgumentStateException("The function's containing class is not in the string signature and there are no default classes. " + getParamDbg(class_funcParamStringSignature, default_package, default_classesInOrder));
385         }
386
387         defaultClasses = default_classesInOrder;
388
389         if(default_package != null)  {
390            throw  new IllegalArgumentStateException("Both default_package and default_classesInOrder are non-null. " + getParamDbg(class_funcParamStringSignature, default_package, default_classesInOrder));
391         }
392
393      }  else if(default_classesInOrder != null)  {
394         throw  new IllegalArgumentStateException("The function's containing class provided in both string-signature and default_classesInOrder. " + getParamDbg(class_funcParamStringSignature, default_package, default_classesInOrder));
395      }
396
397      if(clsNm != null  &&  pkgNm == null)  {
398         if(default_package == null  ||  default_package.length()  == 0)  {
399            throw  new IllegalArgumentStateException("No package provided (or is the empty-string). " + getParamDbg(class_funcParamStringSignature, default_package, default_classesInOrder));
400         }
401         pkgNm = default_package;
402
403      }  else if(pkgNm != null  &&  default_package != null)  {
404         throw  new IllegalArgumentStateException("Package provided in both string-signature and default_package. (Package name found in sig: \"" + pkgNm + "\"). " + getParamDbg(class_funcParamStringSignature, default_package, default_classesInOrder));
405      }
406
407      if(pkgNm != null)  {
408
409         try  {
410            defaultClasses = new Class[] {Class.forName(pkgNm + clsNm)};
411         }  catch(ClassNotFoundException cnfx)  {
412            throw  new ClassNotFoundException("Attempting Class.forName(\"" + pkgNm + clsNm + "\"). " + getParamDbg(class_funcParamStringSignature, default_package, default_classesInOrder));
413         }
414      }
415
416      SimpleMethodSignature sig = new SimpleMethodSignature(return_typeCls, defaultClasses, funcName, paramList, debugDest_ifNonNull);
417      if(dbgAptr != null)  {
418         dbgAptr.appentln("Returning SimpleMethodSignature: " + sig);
419      }
420      return  sig;
421   }
422      private static final String getParamDbg(Object class_funcParamStringSignature, String default_package, Class<?>[] default_classesInOrder)  {
423         return  "class_funcParamStringSignature=\"" + class_funcParamStringSignature + "\", default_package=\"" + default_package + "\", default_classesInOrder=" + Arrays.toString(default_classesInOrder);
424      }
425      //Unused to-search strings, so matchers can be reset(s) instead of recreated
426      private static final Matcher classFuncParamsMtchr = Pattern.compile(STRING_SIGNATURE_REGEX).matcher("");
427   /**
428      <p>Get the objects from the string-representation of a function's parameter list.</p>
429
430    * @param  commaSep_varList  May not be {@code null}, each element must be separated by a <i>comma-space</i> ({@code ", "}), and each element must be formatted as required by {@link #getObjectFromString(String) getObjectFromString}{@code (s)}. Example value:
431      <br/> &nbsp; &nbsp; &nbsp; &nbsp; <code>&quot;\&quot;parameter\&quot;, \&quot;list\&quot;, 1, (byte)-3, true&quot;</code>
432    * @return  A list of objects, where each element is the object represented by the corresponding element in {@code commaSep_varList}, in the same order as the exist in the string.
433    * @see  <code><!-- GENERIC PARAMETERS FAIL IN @link --><a href="http://aliteralmind.com/docs/computer/programming/xbnjava/xbnjava-0.1.5/documentation/javadoc/com/github/xbn/lang/reflect/ReflectRtxUtil.html#getClassArrayFromObjects(O[])">getClassArrayFromObjects</a></code>
434    * @see  #newFromStringAndDefaults(Class, Object, String, Class[], Appendable) newFromStringAndDefaults(cls,s,s,cls[])
435    * @exception  IllegalArgumentException  If {@code commaSep_varList} is invalidly-formatted.
436    */
437   public static final List<Object> getObjectListFromCommaSepParamString(String commaSep_varList)  {
438      if(commaSep_varList.length() == 0)  {
439         //This used to be a static final EMPTY_ARRAY_LIST.
440         //It was causing problems when being used in Codelet, because
441         //JavaDoc is multi-threaded!
442         return  new ArrayList<Object>(0);
443      }
444      String[] strVars = null;
445      try  {
446         strVars = commaSep_varList.split(", ");
447      }  catch(RuntimeException rx)  {
448         throw  CrashIfObject.nullOrReturnCause(commaSep_varList, "commaSep_varList", null, rx);
449      }
450      List<Object> objList = new ArrayList<Object>(strVars.length);
451      for(int i = 0; i < strVars.length; i++)  {
452         try  {
453            objList.add(getObjectFromString(strVars[i]));
454         }  catch(RuntimeException rx)  {
455            throw  new IllegalArgumentException("Attempting to parse parameter element at index " + i + ": " + rx + ". Elements so far: " + Arrays.toString(objList.toArray()) + ". commaSep_varList=\"" + commaSep_varList + "\"");
456         }
457      }
458      return  objList;
459   }
460
461   /**
462      <p>Get a list of {@code Class}es, for each object in a list, as required when obtaining a method.</p>
463
464    * @return  <code>{@link com.github.xbn.lang.reflect.ReflectRtxUtil ReflectRtxUtil}.{@link com.github.xbn.lang.reflect.ReflectRtxUtil#getClassListFromObjects(List) getClassListFromObjects}(objectList)</code>
465   public static final List<Class<?>> getClassListFromObjects(List<?> objectList)  {
466      return  ReflectRtxUtil.getClassListFromObjects(objectList);
467   }
468    */
469   /**
470        <p>Get the object represented by a single string-representation of a single parameter. The only available types are the <a href="http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html">eight primitives</a> and non-{@code null} strings ({@code null} is not possible).</p>
471
472        @param  var_asString  May not be {@code null} or empty, and must conform to the following:
473
474        <p><TABLE ALIGN="center" WIDTH="100%" BORDER="1" CELLSPACING="0" CELLPADDING="4" BGCOLOR="#EEEEEE"><TR ALIGN="center" VALIGN="middle">
475                <TD><b><u>Type</u></b></TD>
476                <TD><b><u>Examples</u></b></TD>
477                <TD><b><u>Notes</u></b></TD>
478        </TR><TR>
479                <TD><b>{@link java.lang.Boolean Boolean}</b></TD>
480                <TD>{@code true} or {@code false}</TD>
481                <TD>&nbsp;</TD>
482        </TR><TR>
483                <TD><b>{@link java.lang.Character Character}</b></TD>
484                <TD>{@code 'x'}</TD>
485                <TD>Must start and end with a single quote, and contain exactly one character between it. For the single-quote, use three single quotes: {@code "'''"} (do not escape it).</TD>
486        </TR><TR>
487                <TD><b>{@link java.lang.Byte Byte}</b></TD>
488                <TD>{@code (byte)3}</TD>
489                <TD>Must contain the explicit cast and be an {@link java.lang.Byte#parseByte(String, int) appropriate value} for a {@code byte}. <i>The plus-sign, indicating a positive number, is not allowed for any numeric type.</i></TD>
490        </TR><TR>
491                <TD><b>{@link java.lang.Short Short}</b></TD>
492                <TD>{@code (short)-15}</TD>
493                <TD>Must contain the explicit cast and be an {@link java.lang.Short#parseShort(String, int) appropriate value} for a {@code short}</TD>
494        </TR><TR>
495                <TD><b>{@link java.lang.Integer Integer}</b></TD>
496                <TD>{@code -15}</TD>
497                <TD>Must be an {@link java.lang.Integer#parseInt(String, int) appropriate value} for an {@code int}</TD>
498        </TR><TR>
499                <TD><b>{@link java.lang.Long Long}</b></TD>
500                <TD>{@code 3300012L}</TD>
501                <TD>Must be followed by a capital {@code 'L'} and be an {@link java.lang.Long#parseLong(String, int) appropriate value} for an {@code long}</TD>
502        </TR><TR>
503                <TD><b>{@link java.lang.Float Float}</b></TD>
504                <TD>{@code 0.003f}</TD>
505                <TD>Must be followed by a lowercase {@code 'f'}, contain at least one digit on both sides of the decimal, and be an {@link java.lang.Float#valueOf(java.lang.String) appropriate value} for an {@code float}. <i>For both {@code float} and {@code double}, only digits (as matched by the regular expression {@code "\d"}) are allowed. Hex numbers, exponenents, and special values such as {@code NaN} or {@code Infinity} are not allowed.</i></TD>
506        </TR><TR>
507                <TD><b>{@link java.lang.Double Double}</b></TD>
508                <TD>{@code -3.8d}</TD>
509                <TD>Must be followed by a lowercase {@code 'd'}, contain at least one digit on both sides of the decimal, and be an {@link java.lang.Double#valueOf(java.lang.String) appropriate value} for an {@code double}</TD>
510        </TR><TR>
511                <TD><b>{@link java.lang.String String}</b></TD>
512                <TD>{@code "Hello there!"}</TD>
513                <TD>Must be non-{@code null}, surrounded by double-quotes, and may not contain a comma ({@code ','}), double-quote ({@code '"'}), or ampersand ({@code '&amp;'}). When these characters are needed, use their respective html entity codes: {@code "&amp;#44;"}, {@code "&amp;quot"}, and {@code "&amp;amp;"}.</TD>
514        </TR></TABLE></p>
515    **/
516   public static final Object getObjectFromString(String var_asString)  {
517      //Remaining possibilites: BOOLEAN, CHAR, BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, STRING
518      try  {
519         if(var_asString.charAt(0) == '(')  {
520            if(var_asString.charAt(1) == 'b')  {
521               return  getByteFromString(var_asString);
522            }  else if(var_asString.charAt(1) == 's')  {
523               return  getShortFromString(var_asString);
524            }  else  {
525               throw  new IllegalArgumentException("var_asString=\"" + var_asString + "\". Starts with '(', but is not properly formatted for a byte or short.");
526            }
527         }
528      }  catch(RuntimeException rx)  {
529         throw  CrashIfObject.nullOrReturnCause(var_asString, "var_asString", null, rx);
530      }
531
532      //Remaining possibilites: BOOLEAN, CHAR, INT, LONG, FLOAT, DOUBLE, STRING
533
534      if(var_asString.charAt(0) == '\'')  {
535         if(var_asString.length() != 3  ||  var_asString.charAt(2) != '\'')  {
536            throw  new IllegalArgumentException("var_asString=\"" + var_asString + "\". Starts with a single-quote (\"'\"), but is not properly formatted for a character (for the literal single-quote, use \"'''\").");
537         }
538         return  new Character(var_asString.charAt(1));
539      }
540
541      //Remaining possibilites: BOOLEAN, INT, LONG, FLOAT, DOUBLE, STRING
542
543      if(var_asString.equals("true")  ||  var_asString.equals("false"))  {
544         return  new Boolean(var_asString);
545      }
546
547      //Remaining possibilites: INT, LONG, FLOAT, DOUBLE, STRING
548
549      if(RegexUtil.resetGetMatcherCINullString(longIntMtchr, var_asString, "var_asString").matches())  {
550         boolean isLong = (var_asString.charAt(var_asString.length() - 1) == 'L');
551         try  {
552            if(isLong)  {
553               return  new Long(var_asString.substring(0, (var_asString.length() - 1)));
554            }  else  {
555               return  new Integer(var_asString);
556            }
557         }  catch(NumberFormatException nfx)  {
558            throw  new IllegalArgumentException(getNumRangeErrMsg(var_asString, (isLong?"long":"int")));
559         }
560      }
561
562      //Remaining possibilites: FLOAT, DOUBLE, STRING
563
564      if(RegexUtil.resetGetMatcherCINullString(floatDoubleMtchr, var_asString, "var_asString").matches())  {
565         boolean isFloat = (var_asString.charAt(var_asString.length() - 1) == 'f');
566         try  {
567            if(isFloat)  {
568            return  new Float(var_asString.substring(0, (var_asString.length() - 1)));
569         }  else  {
570            return  new Double(var_asString.substring(0, (var_asString.length() - 1)));
571            }
572         }  catch(NumberFormatException nfx)  {
573            throw  new IllegalArgumentException(getNumRangeErrMsg(var_asString, (isFloat?"float":"double")));
574         }
575      }
576
577      //Remaining possibilites: STRING
578      if(var_asString.charAt(0) == '"')  {
579         if(var_asString.length() < 2  ||  var_asString.charAt(var_asString.length() - 1) != '"')  {
580            throw  new IllegalArgumentException("var_asString=\"" + var_asString + "\". Starts with '\"', but is not properly formatted for a string.");
581         }
582         var_asString = var_asString.substring(1, (var_asString.length() - 1));
583         if(var_asString.indexOf("\"") != -1  ||  var_asString.indexOf(",") != -1)  {
584            //@since  0.1.2
585            //||  var_asString.indexOf("&") != -1)  {
586
587            throw  new IllegalArgumentException("var_asString=\"" + var_asString + "\" contains a literal comma (','), double-quote ('\"'), or ampersand ('&'). Escape it with \"&#44;\", \"&quot\", or \"&amp;\".");
588         }
589         return  var_asString.replace("&#44;", ",").replace("&quot;", "\"").replace("&amp;", "&");
590      }
591
592      throw  new IllegalArgumentException("var_asString=\"" + var_asString + "\". Unknown type.");
593   }
594      //Unused to-search strings, so matchers can be reset(s) instead of recreated
595      private static final Matcher byteMtchr = Pattern.compile("^\\(byte\\)-?\\d+$").matcher("");
596      private static final Matcher shortMtchr = Pattern.compile("^\\(short\\)-?\\d+$").matcher("");
597      private static final Matcher longIntMtchr = Pattern.compile("^-?\\d+L?$").matcher("");
598      private static final Matcher floatDoubleMtchr = Pattern.compile("^-?\\d+\\.\\d+[fd]$").matcher("");
599   private static final Byte getByteFromString(String var_asString)  {
600      if(!RegexUtil.resetGetMatcherCINullString(byteMtchr, var_asString, "var_asString").matches())  {
601         throw  new IllegalArgumentException("var_asString=\"" + var_asString + "\". Starts with \"(b\", but is not properly formatted for a byte.");
602      }
603      try  {
604         return  new Byte(Byte.parseByte(var_asString.substring(var_asString.indexOf(")") + 1)));
605      }  catch(NumberFormatException nfx)  {
606         throw  new IllegalArgumentException(getNumRangeErrMsg(var_asString, "byte"));
607      }
608   }
609   private static final Short getShortFromString(String var_asString)  {
610      if(!RegexUtil.resetGetMatcherCINullString(shortMtchr, var_asString, "var_asString").matches())  {
611         throw  new IllegalArgumentException("var_asString=\"" + var_asString + "\". Starts with \"(s\", but is not properly formatted for a short.");
612      }
613      try  {
614         return  new Short(Short.parseShort(var_asString.substring(var_asString.indexOf(")") + 1)));
615      }  catch(NumberFormatException nfx)  {
616         throw  new IllegalArgumentException(getNumRangeErrMsg(var_asString, "short"));
617      }
618   }
619   private static final String getNumRangeErrMsg(String var_asString, String type)  {
620      return  "var_asString=\"" + var_asString + "\". Appears to be a " + type + ", but may be out of range.";
621   }
622}