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}