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.util;
016   import  com.github.xbn.util.JavaRegexes;
017   import  com.github.xbn.lang.CrashIfObject;
018   import  com.github.xbn.regexutil.NewPatternFor;
019   import  java.lang.reflect.Constructor;
020   import  java.lang.reflect.Field;
021   import  java.lang.reflect.Method;
022   import  java.util.regex.Matcher;
023   import  java.util.regex.Pattern;
024/**
025   <p>Get urls to function, class, and object targets.</p>
026
027 * @since  0.1.0
028 * @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>
029 **/
030public class JavaDocUtil  {
031   /**
032      <p>Get the url to a class.</p>
033
034    * @return  <code>{@link #appendUrlToClass(StringBuilder, String, Class) appendUrlToClass}((new StringBuilder()), url_toDocRoot, target).toString()</code>
035    */
036   public static final String getUrlToClass(String url_toDocRoot, Class<?> target)  {
037      return  appendUrlToClass((new StringBuilder()), url_toDocRoot, target).toString();
038   }
039   /**
040      <p>Append the url to a class.</p>
041
042    * @param  to_appendTo  May not be {@code null}.
043    * @param  url_toDocRoot  The url to {@code {@docRoot}} for the link target-file. Which must end with a slash {@code "/"}, and <i>should</i> not be {@code null}.. This is also the directory in which the {@code package-list} files exists. For local files, this is a relative directory (such as {@code "../../"}) otherwise this is the full url to the external doc-root directory.
044
045    * @param  target  May not be {@code null}.
046    * @return  <code>to_appendTo.append(url_toDocRoot).
047      <br/> &nbsp; &nbsp; append(target.{@link java.lang.Class#getName() getName}.replace(".", "/")).append(".html")</code>
048    * @see  #getUrlToClass(String, Class)
049    */
050   public static final StringBuilder appendUrlToClass(StringBuilder to_appendTo, String url_toDocRoot, Class<?> target)  {
051      try  {
052         return  to_appendTo.append(url_toDocRoot).
053            append(target.getName().replace(".", "/")).append(".html");
054      }  catch(RuntimeException rx)  {
055         throw  CrashIfObject.nullOrReturnCause(target, "target", null, rx);
056      }
057   }
058   /**
059      <p>Get the url to a constructor.</p>
060
061    * @return  <code>{@link #appendUrlToConstructor(StringBuilder, String, Constructor) appendUrlToConstructor}((new StringBuilder()), url_toDocRoot, target).toString()</code>
062    */
063   public static final String getUrlToConstructor(String url_toDocRoot, Constructor target)  {
064      return  appendUrlToConstructor((new StringBuilder()), url_toDocRoot, target).toString();
065   }
066   /**
067      <p>Append the url to a constructor.</p>
068
069    * @param  to_appendTo  May not be {@code null}.
070    * @param  url_toDocRoot  The url to {@code {@docRoot}} for the link target-file. Which must end with a slash {@code "/"}, and <i>should</i> not be {@code null}.. This is also the directory in which the {@code package-list} files exists. For local files, this is a relative directory (such as {@code "../../"}) otherwise this is the full url to the external doc-root directory.
071    * @param  target  May not be {@code null}.
072    * @return  {@code to_appendTo}
073    * @see  #getUrlToConstructor(String, Constructor) getUrlToConstructor
074    * @see  #appendUrlToMethod(StringBuilder, String, Method) appendUrlToMethod
075    * @see  #appendUrlToClass(StringBuilder, String, Class) appendUrlToClass
076    * @see  #appendClassNameForParams(StringBuilder, Class[], VarArgs) appendClassNameForParams
077    */
078   public static final StringBuilder appendUrlToConstructor(StringBuilder to_appendTo, String url_toDocRoot, Constructor<?> target)  {
079      try  {
080         appendUrlToClass(to_appendTo, url_toDocRoot, target.getDeclaringClass());
081         to_appendTo.append("#").append(target.getDeclaringClass().getSimpleName()).append("(");
082         appendClassNameForParams(to_appendTo, target.getParameterTypes(), VarArgs.getForBoolean(target.isVarArgs())).append(")");
083      }  catch(RuntimeException rx)  {
084         throw  CrashIfObject.nullOrReturnCause(target, "target", null, rx);
085      }
086      return  to_appendTo;
087   }
088   /**
089      <p>Get the url to a function.</p>
090
091    * @return  <code>{@link #appendUrlToConstructor(StringBuilder, String, Constructor) appendUrlToConstructor}((new StringBuilder()), url_toDocRoot, target).toString()</code>
092    */
093   public static final String getUrlToMethod(String url_toDocRoot, Method target)  {
094      return  appendUrlToMethod((new StringBuilder()), url_toDocRoot, target).toString();
095   }
096   /**
097      <p>Append the url to a function.</p>
098
099      <h4>Example</h4>
100
101{@.codelet.and.out com.github.aliteralmind.codelet.examples.util.FunctionConstructorJavaDocLink%_JavaDocUtil()}
102
103
104    * @param  to_appendTo  May not be {@code null}.
105    * @param  url_toDocRoot  The url to {@code {@docRoot}} for the link target-file. Which must end with a slash {@code "/"}, and <i>should</i> not be {@code null}.. This is also the directory in which the {@code package-list} files exists. For local files, this is a relative directory (such as {@code "../../"}) otherwise this is the full url to the external doc-root directory.
106    * @param  target  May not be {@code null}.
107    * @return  {@code to_appendTo}
108    * @see  #getUrlToMethod(String, Method) getUrlToMethod
109    * @see  #appendUrlToConstructor(StringBuilder, String, Constructor) appendUrlToMethod
110    * @see  #appendUrlToClass(StringBuilder, String, Class) appendUrlToClass
111    * @see  #appendClassNameForParams(StringBuilder, Class[], VarArgs) appendClassNameForParams
112    */
113   public static final StringBuilder appendUrlToMethod(StringBuilder to_appendTo, String url_toDocRoot, Method target)  {
114      try  {
115         appendUrlToClass(to_appendTo, url_toDocRoot, target.getDeclaringClass());
116         to_appendTo.append("#").append(target.getName()).append("(");
117         appendClassNameForParams(to_appendTo, target.getParameterTypes(), VarArgs.getForBoolean(target.isVarArgs())).append(")");
118      }  catch(RuntimeException rx)  {
119         throw  CrashIfObject.nullOrReturnCause(target, "target", null, rx);
120      }
121      return  to_appendTo;
122   }
123   /**
124      <p>Get the url to a field.</p>
125
126    * @return  <code>{@link #appendUrlToField(StringBuilder, String, Field) appendUrlToField}((new StringBuilder()), url_toDocRoot, target).toString()</code>
127    */
128   public static final String getUrlToField(String url_toDocRoot, Field target)  {
129      return  appendUrlToField((new StringBuilder()), url_toDocRoot, target).toString();
130   }
131   /**
132      <p>Append the url to a field.</p>
133
134    * @return  <code>{@link #appendUrlToClass(StringBuilder, String, Class) appendUrlToClass}(to_appendTo, url_toDocRoot, target.{@link java.lang.reflect.Field#getDeclaringClass() getDeclaringClass}()).
135      <br/> &nbsp; &nbsp; append(&quot;#&quot;).append(target.{@link java.lang.reflect.Field#getName() Field#getName}())</code>
136    * @see  #getUrlToField(String, Field)
137    */
138   public static final StringBuilder appendUrlToField(StringBuilder to_appendTo, String url_toDocRoot, Field target)  {
139      return  appendUrlToClass(to_appendTo, url_toDocRoot, target.getDeclaringClass()).
140         append("#").append(target.getName());
141   }
142   /**
143      <p>Get the url from a link-source file's package to doc-root.</p>
144
145    * @return  <code>packageElementMtchr.reset(package_containingLink).{@link java.util.regex.Matcher#replaceAll(String) replaceAll}(&quot;../&quot;)</code>
146      <br/>Where {@code packageElementMtchr} is initalized to
147      <br/> &nbsp; &nbsp; <code>{@link java.util.regex.Pattern Pattern}.{@link java.util.regex.Pattern#compile(String) compile}(JavaRegexes.IDENTIFIER + &quot;\\.?&quot;).{@link java.util.regex.Pattern#matcher(CharSequence) matcher}(&quot;&quot;)</code>
148    */
149   public static final String getRelativeUrlToDocRoot(String package_containingLink)  {
150      return  packageElementMtchr.reset(package_containingLink).replaceAll("../");
151   }
152      private static final Matcher packageElementMtchr = Pattern.
153         compile(JavaRegexes.IDENTIFIER + "\\.?").matcher("");
154   /**
155      <p>Get the url from a link-source to the target class file. This always goes entirely down to {@code {@docRoot}}, and then back to the target file.</p>
156
157    * @param  url_toDocRoot  The url to {@code {@docRoot}} for the link target-file. Which must end with a slash {@code "/"}, and <i>should</i> not be {@code null}.. This is also the directory in which the {@code package-list} files exists. For local files, this is a relative directory (such as {@code "../../"}) otherwise this is the full url to the external doc-root directory.
158    * @param  target  May not be {@code null}.
159    * @return  <code>packageElementMtchr.reset(target.{@link java.lang.Class#getName() getName}()).{@link java.util.regex.Matcher#replaceAll(String) replaceAll}(&quot;../&quot;)</code>
160      <br/>Where {@code packageElementMtchr} is initalized to
161      <br/> &nbsp; &nbsp; <code>{@link java.util.regex.Pattern Pattern}.{@link java.util.regex.Pattern#compile(String, int) compile}(&quot;.&quot;, Pattern.{@link java.util.regex.Pattern#LITERAL LITERAL}).{@link java.util.regex.Pattern#matcher(CharSequence) matcher}(&quot;&quot;)</code>
162    */
163   public static final String getUrlToClassViaDocRoot(String url_toDocRoot, Class<?> target)  {
164      try  {
165         return  url_toDocRoot + dotMtchr.reset(target.getName()).replaceAll("/") + ".html";
166      }  catch(RuntimeException rx)  {
167         throw  CrashIfObject.nullOrReturnCause(target, "target", null, rx);
168      }
169   }
170      private static final Matcher dotMtchr = NewPatternFor.literal(".").matcher("");
171   /**
172      <p>For the final parameter in a {@link java.lang.reflect.Method#isVarArgs() variable-argument method} or {@link java.lang.reflect.Constructor#isVarArgs() constructor}, that is known to be an array-ellipsis ({@code "..."}), get its JavaDoc-url parameter name.</p>
173
174      <h4>Example</h4>
175
176{@.codelet.and.out com.github.aliteralmind.codelet.examples.util.JavaDocUtilGetEllipsisArrayLastParamXmpl%()}
177
178    * @param  cls  May not be {@code null}.
179    * @return  The {@linkplain java.lang.Class#getCanonicalName() canonical class name}, where the final array indicator ({@code "[]"}) is replaced with an ellipsis ({@code "..."}).
180    * @exception  IllegalArgumentException  If {@code cls} is not {@linkplain java.lang.Class#isArray() an array}.
181    * @see  #appendClassNameForParams(StringBuilder, Class[], VarArgs)
182    * @see  java.lang.Class#getCanonicalName() Class#getCanonicalName()
183    * @see  java.lang.Class#getCanonicalName()
184    */
185   public static final String getEllipsisArrayLastParam(Class<?> cls)  {
186      try  {
187         if(!cls.isArray())  {
188            throw  new IllegalArgumentException("cls.isArray() is false.");
189         }
190      }  catch(RuntimeException rx)  {
191         throw  CrashIfObject.nullOrReturnCause(cls, "cls", null, rx);
192      }
193
194      //cls.isArray() is true
195
196      //2: Same as "[]".length.
197
198      return  cls.getCanonicalName().substring(0, (cls.getCanonicalName().length() - 2)) + "...";
199   }
200   /**
201      <p>Append the comma-separated list of JavaDoc parameters for a method or constructor.</p>
202
203    * @return  <code>{@link #appendClassNameForParams(StringBuilder, Class[], VarArgs) appendClassNameForParams}((new StringBuilder()), classes, is_varArgs).toString()</code>
204    */
205   public static final String getClassNameForParams(Class<?>[] classes, VarArgs var_args)  {
206      return  appendClassNameForParams((new StringBuilder()), classes, var_args).toString();
207   }
208   /**
209      <p>Append the comma-separated list of JavaDoc parameters for a method or constructor. This does not include the surrounding parentheses.</p>
210
211      <p>This appends the {@linkplain java.lang.Class#getCanonicalName() canonical name} of each class. If {@code is_varArgs} is {@code true}, the final parameter is output with {@link #getEllipsisArrayLastParam(Class) getEllipsisArrayLastParam}.</p>
212
213    * @param  to_appendTo  May not be {@code null}.
214    * @param  classes  May not be {@code null}, and {@code is_varArgs} is {@code true}, the final element must be an array (and {@code classes.length} <i>should</i> be non-empty).
215    * @param  var_args  If {@link VarArgs#YES YES}, this is a {@link java.lang.reflect.Method#isVarArgs() variable-argument} method or {@link java.lang.reflect.Constructor#isVarArgs() constructor}, meaning the final parameter is an ellipsis array: {@code "..."}. This parameter value may not be {@code null}.
216    * @return  {@code to_appendTo}
217    * @see  #getClassNameForParams(Class[], VarArgs)
218    * @see  #appendUrlToConstructor(StringBuilder, String, Constructor) appendUrlToConstructor
219    * @see  #appendUrlToMethod(StringBuilder, String, Method) appendUrlToMethod
220    * @see  com.github.xbn.lang.reflect.ReflectRtxUtil#appendClassNames(StringBuilder, String, Class[], String, String) lang.reflect.ReflectRtxUtil#appendClassNames
221    */
222   public static final StringBuilder appendClassNameForParams(StringBuilder to_appendTo, Class<?>[] classes, VarArgs var_args)  {
223      int lenMinus1;
224      try  {
225         lenMinus1 = classes.length - 1;
226      }  catch(RuntimeException rx)  {
227         throw  CrashIfObject.nullOrReturnCause(classes, "classes", null, rx);
228      }
229      for(int i = 0; i < classes.length; i++)  {
230         Class<?> cls = classes[i];
231
232         try  {
233            if(var_args.isYes()  &&  i == lenMinus1)  {
234               to_appendTo.append(getEllipsisArrayLastParam(cls));
235            }  else  {
236               to_appendTo.append(cls.getCanonicalName());
237            }
238         }  catch(RuntimeException rx)  {
239            CrashIfObject.nnull(var_args, "var_args", null);
240            throw  CrashIfObject.nullOrReturnCause(to_appendTo, "to_appendTo", null, rx);
241         }
242
243         if(i < lenMinus1)  {
244            to_appendTo.append(", ");
245         }
246      }
247      return  to_appendTo;
248   }
249   /*
250      <p>Creates a new map of all package-lists, for all {@code {@docRoot}}
251      urls in a string iterator.</p>
252
253      <p>For each url-item in the iterator, this calls
254      <br/> &nbsp; &nbsp; <code>{@link #addPackageListToMapFromDocRootUrl(Map, String, Appendable, Appendable) addPackageListToMapFromDocRootUrl}(<i>[the-map]</i>, <i>[the-url]</i>, debugUrls_ifNonNull, debugPkgs_ifNonNull)</code></p>
255
256    * @param  url_toDocRoot  The url to {@code {@docRoot}} for the link target-file. Which must end with a slash {@code "/"}, and <i>should</i> not be {@code null}.. This is also the directory in which the {@code package-list} files exists. For local files, this is a relative directory (such as {@code "../../"}) otherwise this is the full url to the external doc-root directory.
257    * @return  A new tree-map where each entry's key is a package, and each value is its doc-root url.
258   public static final TreeMap<String,String> getPackageListMapFromDocRootUrlIterator(Iterator<String> url_toDocRootItr, Appendable debugUrls_ifNonNull, Appendable debugPkgs_ifNonNull) throws MalformedURLException, IOException  {
259      TreeMap<String,String> map = new TreeMap<String,String>();
260      try  {
261         while(url_toDocRootItr.hasNext())  {
262            addPackageListToMapFromDocRootUrl(map, url_toDocRootItr.next(), debugUrls_ifNonNull, debugPkgs_ifNonNull);
263         }
264      }  catch(RuntimeException rx)  {
265         throw  CrashIfObject.nullOrReturnCause(url_toDocRootItr, "url_toDocRootItr", null, rx);
266      }
267      return  map;
268   }
269    */
270   /*
271      <p>Retrieve's the package-list from a url, and adds each package to a map.</p>
272
273    * @return  <code>{@link #addPackageListToMap(Map, Iterator, String, Appendable, Appendable) addPackageListToMap}(package_docRootUrlMap,
274      <br/> &nbsp; &nbsp; {@link com.github.xbn.text.StringUtil}.{@link com.github.xbn.text.StringUtil#getLineIterator(Object) getLineIterator}(text), url_toDocRoot + &quot;package-list&quot;,
275      <br/> &nbsp; &nbsp; debugUrls_ifNonNull, debugPkgs_ifNonNull)</code>
276      <br/>Where {@code text} is
277      <br/> &nbsp; &nbsp; <code>{@link com.github.xbn.io.IOUtil}.{@link com.github.xbn.io.IOUtil#getWebPageSourceX(String, String) getWebPageSourceX}(url_toDocRoot, null)</code>
278   public static final void addPackageListToMapFromDocRootUrl(Map<String,String> package_docRootUrlMap, String url_toDocRoot, IfUrlInaccessible if_inaccessible, Appendable debugUrls_ifNonNull, Appendable debugPkgs_ifNonNull) throws MalformedURLException, IOException  {
279      String text = null;
280      try  {
281         text = IOUtil.getWebPageSourceX(url_toDocRoot, null);
282      }  catch(Exception x)  {
283         try  {
284            if(if_inaccessible.isWarn())  {
285               TextAppenter dbgUrlAptr = NewTextAppenterFor.appendableSuppressIfNull(debugUrls_ifNonNull);
286               dbgUrlAptr.appentln("Warning: Unable to retrieve package-list from " + url_toDocRoot + ": " + x);
287            }  else  {
288               throw  x;
289            }
290         }
291      }
292      addPackageListToMap(package_docRootUrlMap,
293         StringUtil.getLineIterator(text), url_toDocRoot + "package-list",
294         debugUrls_ifNonNull, debugPkgs_ifNonNull);
295   }
296    */
297   /*
298      <p>Add all items from a url's package-list to the package-list/doc-root-url map. The key is the package name, the value is the url.</p>
299
300    * @param  package_docRootUrlMap  May not be {@code null}.
301    * @param  packageList_lineItr  May not be {@code null} or contain any already-existing key in the map.
302    * @param  url_toDocRoot  The url to {@code {@docRoot}} for the link target-file. Which must end with a slash {@code "/"}, and <i>should</i> not be {@code null}.. This is also the directory in which the {@code package-list} files exists. For local files, this is a relative directory (such as {@code "../../"}) otherwise this is the full url to the external doc-root directory.
303    * @param  debugUrls_ifNonNull  If non-{@code null}, this outputs the url only.
304    * @param  debugPkgs_ifNonNull  If non-{@code null}, this outputs the size of the map before any package is added to it, and each package before adding it.
305    * @see  #addPackageListToMapFromDocRootUrl(Map, String, Appendable, Appendable)
306    * @see  #getPackageListMapFromDocRootUrlIterator(Iterator, Appendable, Appendable)
307   public static final void addPackageListToMap(Map<String,String> package_docRootUrlMap, Iterator<String> packageList_lineItr, String url_toDocRoot, Appendable debugUrls_ifNonNull, Appendable debugPkgs_ifNonNull)  {
308      try  {
309         if(url_toDocRoot.length() < 2  ||  !url_toDocRoot.endsWith("/"))  {
310            throw  new IllegalArgumentException("url_toDocRoot (\"" + url_toDocRoot + "\") is less than two characters, or does not end with a url-slash ('/').");
311         }
312      }  catch(RuntimeException rx)  {
313         throw  CrashIfObject.nullOrReturnCause(url_toDocRoot, "url_toDocRoot", null, rx);
314      }
315      CrashIfString.nullEmpty(url_toDocRoot, "url_toDocRoot", null);
316      Objects.requireNonNull(url_toDocRoot, "url_toDocRoot");
317      TextAppenter dbgUrlAptr = NewTextAppenterFor.appendableSuppressIfNull(debugUrls_ifNonNull);
318      TextAppenter dbgPkgAptr = NewTextAppenterFor.appendableSuppressIfNull(debugPkgs_ifNonNull);
319      try  {
320         if(dbgUrlAptr != null)  {
321            dbgUrlAptr.appentln("addPackageListToMap: url_toDocRoot=\"" + url_toDocRoot + "\"");
322         }
323         if(debugPkgs_ifNonNull != null)  {
324            dbgPkgAptr.appentln(" - package_docRootUrlMap.size()=" + package_docRootUrlMap.size());
325         }
326      }  catch(RuntimeException rx)  {
327         throw  CrashIfObject.nullOrReturnCause(package_docRootUrlMap, "package_docRootUrlMap", null, rx);
328      }
329
330      int addedCount = 1;
331      try  {
332         while(packageList_lineItr.hasNext())  {
333            String pkg = packageList_lineItr.next();
334
335            CrashIfString.nullEmpty(pkg, "pkg", null);
336
337                                if(debugPkgs_ifNonNull != null)  {
338               dbgPkgAptr.appentln(" - " + (addedCount++) + ". " + pkg);
339            }
340
341            if(package_docRootUrlMap.containsKey(pkg))  {
342               throw  new IllegalArgumentException("package \"" + pkg + "\" already exists in package_docRootUrlMap:" + LINE_SEP +
343                  MapUtil.appendToString(new StringBuilder(), package_docRootUrlMap));
344            }
345            package_docRootUrlMap.put(pkg, url_toDocRoot);
346         }
347      }  catch(RuntimeException rx)  {
348         throw  CrashIfObject.nullOrReturnCause(packageList_lineItr, "packageList_lineItr", null, rx);
349      }
350   }
351    */
352   private JavaDocUtil()  {
353      throw  new IllegalStateException("Do not instantiate.");
354   }
355}