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}