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.taglet; 016 import com.sun.tools.doclets.Taglet; 017 import java.util.Map; 018 import com.github.xbn.lang.CrashIfObject; 019 import com.github.aliteralmind.codelet.util.JavaDocUtil; 020 import com.github.xbn.util.JavaRegexes; 021 import com.sun.javadoc.ClassDoc; 022 import com.sun.javadoc.Doc; 023 import com.sun.javadoc.PackageDoc; 024 import com.sun.javadoc.ProgramElementDoc; 025 import com.sun.javadoc.Tag; 026 import java.util.regex.Matcher; 027 import java.util.regex.Pattern; 028/** 029 <p>Generically-useful utilities related to {@code com.sun.javadoc}.</p> 030 031 * @since 0.1.0 032 * @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> 033 **/ 034public class ComSunJavaDocUtil { 035 /** 036 <p>Register a Taglet as required by JavaDoc.</p> 037 038 <h3><i>(Why is the map parameter type-erased? What generics does it need?)</i></h3> 039 040 <p>Steps<ol> 041 <li>If a taglet with the name equal to <code>taglet.<!-- No package-list for Taglet --><a href="http://docs.oracle.com/javase/7/docs/jdk/api/javadoc/taglet/com/sun/tools/doclets/Taglet.html#getName()">getName</a>()</code> already exists in the map, remove it.</li> 042 <li>{@code taglet} is <a href="http://docs.oracle.com/javase/7/docs/api/java/util/Map.html#put(K, V)">added</a> to the map, with its name as the key.</li> 043 </ol></p> 044 045 * @param taglet The taglet to register. May not be {@code null}. 046 * @param map The map to register this tag to. May not be {@code null}. 047 * @since 0.1.1 048 */ 049 @SuppressWarnings({"unchecked", "rawtypes"}) 050 public static void registerNewTagletInstance(Taglet taglet, Map map) { 051 try { 052 final String name = taglet.getName(); 053 final Taglet alreadyRegisteredTaglet = (Taglet)map.get(name); 054 if (alreadyRegisteredTaglet != null) { 055 map.remove(name); 056 } 057 map.put(name, taglet); 058 } catch(RuntimeException rx) { 059 CrashIfObject.nnull(taglet, "taglet", null); 060 throw CrashIfObject.nullOrReturnCause(map, "map", null, rx); 061 } 062/* 063 FileTextletTaglet tag = new FileTextletTaglet(); 064 Taglet t = (Taglet) map.get(tag.getName()); 065 if (t != null) { 066 map.remove(tag.getName()); 067 } 068 map.put(tag.getName(), tag); 069 */ 070 } 071 072 /** 073 <p>Get the relative url whose value is equivalent to {@code {@docRoot}}.</p> 074 075 * @return <code>{@link com.github.aliteralmind.codelet.util.JavaDocUtil JavaDocUtil}.{@link com.github.aliteralmind.codelet.util.JavaDocUtil#getRelativeUrlToDocRoot(String) getRelativeUrlToDocRoot}({@link #getEnclosingPackageName(Tag) getEnclosingPackageName}(tag))</code> 076 */ 077 public static final String getRelativeUrlToDocRoot(Tag tag) { 078 return JavaDocUtil.getRelativeUrlToDocRoot(getEnclosingPackageName(tag)); 079 } 080 /* 081 private static final Matcher packageElementMtchr = Pattern.compile("[\\p{L}_\\p{Sc}][\\p{L}\\p{N}_\\p{Sc}]*\\.?").matcher(""); 082 <p>Get the relative url whose value is equivalent to {@code {@docRoot}}. This is a generic (self-contained) version of {@link #getRelativeUrlToDocRoot(Tag) getRelativeUrlToDocRoot}--they both do the same thing.</p> 083 084 * @return <code>packageElementMtchr.reset({@link #getEnclosingPackageName(Tag) getEnclosingPackageName}(tag)).{@link java.util.regex.Matcher#replaceAll(String) replaceAll}("../")</code> 085 <br/>Where {@code packageElementMtchr} is initialized to 086 <br/> <code>Pattern.{@link java.util.regex.Pattern#compile(String) compile}("[\\p{L}_\\p{Sc}][\\p{L}\\p{N}_\\p{Sc}]*\\.?").{@link java.util.regex.Pattern#matcher(CharSequence) matcher}("")</code> 087 * @see <code><a href="http://stackoverflow.com/questions/4079268/custom-taglet-with-reference-to-docroot">http://stackoverflow.com/questions/4079268/custom-taglet-with-reference-to-docroot</a></code> 088 public static final String getRelativeUrlToDocRootGeneric(Tag tag) { 089 return packageElementMtchr.reset(getEnclosingPackageName(tag)).replaceAll("../"); 090 } 091 */ 092 /** 093 <p>Get the package of the tag-containing JavaDoc page.</p> 094 095 * @return If {@link #getPackageDoc(Tag) getPackageDoc}{@code (tag)} is<ul> 096 <li>{@code null}: {@code ""}</li> 097 <li>non-{@code null}: Its {@linkplain com.sun.javadoc.PackageDoc#name() name}.</li> 098 </ul> 099 */ 100 public static final String getEnclosingPackageName(Tag tag) { 101 PackageDoc pkgDoc = getPackageDoc(tag); 102 return ((pkgDoc == null) ? "" : pkgDoc.name()); 103 } 104 /** 105 <p>Get the tag-containing JavaDoc page's post-package name.</p> 106 107 * @param tag May not be {@code null}. 108 * @param include_postClassName If the taglet is in the class JavaDoc-block, The classes generics, if any, are included in its simple name. If the taglet is in a function's JavaDoc-block, its name and parameters are included. If this parameter is {@link IncludePostClassName#YES YES}, the generics or function name, if any, are included in the returned value. If {@link IncludePostClassName#NO NO}, they are excluded. This parameter may not be {@code null}. 109 * @return If the tag's {@linkplain #getEnclosingPackageName(Tag) enclosing package name}<ul> 110 <li>Has no characters: {@code "OVERVIEW_SUMMARY"}</li> 111 <li>Non empty and its name<ul> 112 <li>Has no characters: {@code "PACKAGE_SUMMARY"}</li> 113 <li>Non-empty: The name</li> 114 </ul></li> 115 </ul> 116 */ 117 public static final String getEnclosingSimpleName(Tag tag, IncludePostClassName include_postClassName) { 118 String pkg = getEnclosingPackageName(tag); 119 if(pkg.length() == 0) { 120//System.out.println("getEnclosingSimpleName.1 OVERVIEW_SUMMARY"); 121 return "OVERVIEW_SUMMARY"; 122 } 123 124 String name = tag.holder().toString().substring(pkg.length()); 125//System.out.println("getEnclosingSimpleName.1a tag.holder()=\"" + tag.holder() + "\"\n - without package=\"" + name+ "\""); 126 127 128 if(name.length() == 0) { 129//System.out.println("getEnclosingSimpleName.2 PACKAGE_SUMMARY"); 130 return "PACKAGE_SUMMARY"; 131 } 132 133 if(name.charAt(0) == '.') { 134 name = name.substring(1); 135 } 136 137 try { 138 if(include_postClassName.isYes()) { 139//System.out.println("getEnclosingSimpleName.2a " + name); 140 return name; 141 } 142 } catch(RuntimeException rx) { 143 throw CrashIfObject.nullOrReturnCause(include_postClassName, "include_postClassName", null, rx); 144 } 145 146 //include_postClassName.NO 147 148 name = oneOrMoreWordCharsAtInputStartMtchr.reset(name).replaceFirst("$1"); 149 150//System.out.println("getEnclosingSimpleName.3 " + name); 151 return name; 152 153/* 154 try { 155 if(include_postClassName.isYes()) { 156System.out.println("getEnclosingSimpleName.3 " + name); 157 return name; 158 } 159 } catch(RuntimeException rx) { 160 throw CrashIfObject.nullOrReturnCause(include_postClassName, "include_postClassName", null, rx); 161 } 162 163 int idxGreaterThan = name.indexOf('<'); 164 int idxOpenParen = name.indexOf('('); 165 166 //Must check for parens first, because it is possible for generics to be 167 //inside parameters! 168 if(idxOpenParen == -1) { 169 if(idxGreaterThan == -1) { 170System.out.println("getEnclosingSimpleName.4 " + name); 171 return name; 172 } 173 name = name.substring(0, idxGreaterThan); 174System.out.println("getEnclosingSimpleName.5 " + name); 175 return name; 176 177 } 178 //Return name up-through-but-not-including the dot-func-signature 179 180 name = name.substring(0, name.lastIndexOf('.', idxOpenParen)); 181System.out.println("getEnclosingSimpleName.6 " + name); 182 return name; 183 */ 184 185 } 186 private static final Matcher oneOrMoreWordCharsAtInputStartMtchr = Pattern.compile("^(\\w+).*$").matcher(""); 187 /** 188 <p>Get the object containing the "package" of the tag-containing JavaDoc page.</p> 189 190 <p>This function was written by <a href="http://stackoverflow.com">Stack Overflow</a> user <a href="http://stackoverflow.com/users/547546/chad-retz">Chad Retz</a>.</p>- 191 192 * @return If the tag's {@linkplain com.sun.javadoc.Tag#holder() holder }<ul> 193 <li>is a {@link com.sun.javadoc.ProgramElementDoc}: Its {@linkplain com.sun.javadoc.ProgramElementDoc#containingPackage() containing package}.</li> 194 <li>is a {@link com.sun.javadoc.PackageDoc}: {@code tag}.</li> 195 <li>Otherwise: {@code null}</li> 196 </ul> 197 * @see <code><a href="https://chadretz.wordpress.com/2010/12/19/mathml-inside-javadoc-using-mathjax-and-a-custom-taglet/">https://chadretz.wordpress.com/2010/12/19/mathml-inside-javadoc-using-mathjax-and-a-custom-taglet/</a></code> 198 */ 199 public static final PackageDoc getPackageDoc(Tag tag) { 200 Doc holder = tag.holder(); 201 if (holder instanceof ProgramElementDoc) { 202 return ((ProgramElementDoc) holder).containingPackage(); 203 } else if (holder instanceof PackageDoc) { 204 return (PackageDoc) holder; 205 } else { 206 return null; 207 } 208 } 209}