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> 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> append("#").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}("../")</CODE> 146 <BR>Where {@code packageElementMtchr} is initalized to 147 <BR> <CODE>{@link java.util.regex.Pattern Pattern}.{@link java.util.regex.Pattern#compile(String) compile}(JavaRegexes.IDENTIFIER + "\\.?").{@link java.util.regex.Pattern#matcher(CharSequence) matcher}("")</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}("../")</CODE> 160 <BR>Where {@code packageElementMtchr} is initalized to 161 <BR> <CODE>{@link java.util.regex.Pattern Pattern}.{@link java.util.regex.Pattern#compile(String, int) compile}(".", Pattern.{@link java.util.regex.Pattern#LITERAL LITERAL}).{@link java.util.regex.Pattern#matcher(CharSequence) matcher}("")</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> <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> {@link com.github.xbn.text.StringUtil}.{@link com.github.xbn.text.StringUtil#getLineIterator(Object) getLineIterator}(text), url_toDocRoot + "package-list", 275 <BR> debugUrls_ifNonNull, debugPkgs_ifNonNull)</CODE> 276 <BR>Where {@code text} is 277 <BR> <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}